feat(linter): add convert-to-inferred migration generator (#23142)
This commit is contained in:
parent
1d2c843c26
commit
acd0993f1a
@ -7310,6 +7310,14 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"isExternal": false,
|
"isExternal": false,
|
||||||
"disableCollapsible": false
|
"disableCollapsible": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "convert-to-inferred",
|
||||||
|
"path": "/nx-api/eslint/generators/convert-to-inferred",
|
||||||
|
"name": "convert-to-inferred",
|
||||||
|
"children": [],
|
||||||
|
"isExternal": false,
|
||||||
|
"disableCollapsible": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"isExternal": false,
|
"isExternal": false,
|
||||||
|
|||||||
@ -766,6 +766,15 @@
|
|||||||
"originalFilePath": "/packages/eslint/src/generators/convert-to-flat-config/schema.json",
|
"originalFilePath": "/packages/eslint/src/generators/convert-to-flat-config/schema.json",
|
||||||
"path": "/nx-api/eslint/generators/convert-to-flat-config",
|
"path": "/nx-api/eslint/generators/convert-to-flat-config",
|
||||||
"type": "generator"
|
"type": "generator"
|
||||||
|
},
|
||||||
|
"/nx-api/eslint/generators/convert-to-inferred": {
|
||||||
|
"description": "Convert existing ESLint project(s) using `@nx/eslint:lint` executor to use `@nx/eslint/plugin`.",
|
||||||
|
"file": "generated/packages/eslint/generators/convert-to-inferred.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "convert-to-inferred",
|
||||||
|
"originalFilePath": "/packages/eslint/src/generators/convert-to-inferred/schema.json",
|
||||||
|
"path": "/nx-api/eslint/generators/convert-to-inferred",
|
||||||
|
"type": "generator"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"path": "/nx-api/eslint"
|
"path": "/nx-api/eslint"
|
||||||
|
|||||||
@ -754,6 +754,15 @@
|
|||||||
"originalFilePath": "/packages/eslint/src/generators/convert-to-flat-config/schema.json",
|
"originalFilePath": "/packages/eslint/src/generators/convert-to-flat-config/schema.json",
|
||||||
"path": "eslint/generators/convert-to-flat-config",
|
"path": "eslint/generators/convert-to-flat-config",
|
||||||
"type": "generator"
|
"type": "generator"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Convert existing ESLint project(s) using `@nx/eslint:lint` executor to use `@nx/eslint/plugin`.",
|
||||||
|
"file": "generated/packages/eslint/generators/convert-to-inferred.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "convert-to-inferred",
|
||||||
|
"originalFilePath": "/packages/eslint/src/generators/convert-to-inferred/schema.json",
|
||||||
|
"path": "eslint/generators/convert-to-inferred",
|
||||||
|
"type": "generator"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"githubRoot": "https://github.com/nrwl/nx/blob/master",
|
"githubRoot": "https://github.com/nrwl/nx/blob/master",
|
||||||
|
|||||||
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "convert-to-inferred",
|
||||||
|
"factory": "./src/generators/convert-to-inferred/convert-to-inferred",
|
||||||
|
"schema": {
|
||||||
|
"$schema": "https://json-schema.org/schema",
|
||||||
|
"$id": "NxEslintConvertToInferred",
|
||||||
|
"description": "Convert existing Eslint project(s) using `@nx/eslint:lint` executor to use `@nx/eslint/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||||
|
"title": "Convert Eslint project from executor to plugin",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"project": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The project to convert from using the `@nx/eslint:lint` executor to use `@nx/eslint/plugin`.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"skipFormat": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether to format files at the end of the migration.",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"presets": []
|
||||||
|
},
|
||||||
|
"description": "Convert existing ESLint project(s) using `@nx/eslint:lint` executor to use `@nx/eslint/plugin`.",
|
||||||
|
"implementation": "/packages/eslint/src/generators/convert-to-inferred/convert-to-inferred.ts",
|
||||||
|
"aliases": [],
|
||||||
|
"hidden": false,
|
||||||
|
"path": "/packages/eslint/src/generators/convert-to-inferred/schema.json",
|
||||||
|
"type": "generator"
|
||||||
|
}
|
||||||
@ -403,6 +403,7 @@
|
|||||||
- [workspace-rules-project](/nx-api/eslint/generators/workspace-rules-project)
|
- [workspace-rules-project](/nx-api/eslint/generators/workspace-rules-project)
|
||||||
- [workspace-rule](/nx-api/eslint/generators/workspace-rule)
|
- [workspace-rule](/nx-api/eslint/generators/workspace-rule)
|
||||||
- [convert-to-flat-config](/nx-api/eslint/generators/convert-to-flat-config)
|
- [convert-to-flat-config](/nx-api/eslint/generators/convert-to-flat-config)
|
||||||
|
- [convert-to-inferred](/nx-api/eslint/generators/convert-to-inferred)
|
||||||
- [eslint-plugin](/nx-api/eslint-plugin)
|
- [eslint-plugin](/nx-api/eslint-plugin)
|
||||||
- [documents](/nx-api/eslint-plugin/documents)
|
- [documents](/nx-api/eslint-plugin/documents)
|
||||||
- [Overview](/nx-api/eslint-plugin/documents/overview)
|
- [Overview](/nx-api/eslint-plugin/documents/overview)
|
||||||
|
|||||||
@ -21,7 +21,7 @@ interface Schema {
|
|||||||
|
|
||||||
export async function convertToInferred(tree: Tree, options: Schema) {
|
export async function convertToInferred(tree: Tree, options: Schema) {
|
||||||
const projectGraph = await createProjectGraphAsync();
|
const projectGraph = await createProjectGraphAsync();
|
||||||
let migratedProjects = await migrateExecutorToPlugin(
|
const migratedProjectsModern = await migrateExecutorToPlugin(
|
||||||
tree,
|
tree,
|
||||||
projectGraph,
|
projectGraph,
|
||||||
'@nx/cypress:cypress',
|
'@nx/cypress:cypress',
|
||||||
@ -35,7 +35,7 @@ export async function convertToInferred(tree: Tree, options: Schema) {
|
|||||||
options.project
|
options.project
|
||||||
);
|
);
|
||||||
|
|
||||||
migratedProjects += await migrateExecutorToPlugin(
|
const migratedProjectsLegacy = await migrateExecutorToPlugin(
|
||||||
tree,
|
tree,
|
||||||
projectGraph,
|
projectGraph,
|
||||||
'@nrwl/cypress:cypress',
|
'@nrwl/cypress:cypress',
|
||||||
@ -49,6 +49,9 @@ export async function convertToInferred(tree: Tree, options: Schema) {
|
|||||||
options.project
|
options.project
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const migratedProjects =
|
||||||
|
migratedProjectsModern.size + migratedProjectsLegacy.size;
|
||||||
|
|
||||||
if (migratedProjects === 0) {
|
if (migratedProjects === 0) {
|
||||||
throw new Error('Could not find any targets to migrate.');
|
throw new Error('Could not find any targets to migrate.');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,7 +29,8 @@ import type { ConfigurationResult } from 'nx/src/project-graph/utils/project-con
|
|||||||
type PluginOptionsBuilder<T> = (targetName: string) => T;
|
type PluginOptionsBuilder<T> = (targetName: string) => T;
|
||||||
type PostTargetTransformer = (
|
type PostTargetTransformer = (
|
||||||
targetConfiguration: TargetConfiguration,
|
targetConfiguration: TargetConfiguration,
|
||||||
tree?: Tree
|
tree?: Tree,
|
||||||
|
projectDetails?: { projectName: string; root: string }
|
||||||
) => TargetConfiguration;
|
) => TargetConfiguration;
|
||||||
type SkipTargetFilter = (
|
type SkipTargetFilter = (
|
||||||
targetConfiguration: TargetConfiguration
|
targetConfiguration: TargetConfiguration
|
||||||
@ -129,7 +130,10 @@ class ExecutorToPluginMigrator<T> {
|
|||||||
delete projectTarget.executor;
|
delete projectTarget.executor;
|
||||||
|
|
||||||
deleteMatchingProperties(projectTarget, createdTarget);
|
deleteMatchingProperties(projectTarget, createdTarget);
|
||||||
projectTarget = this.#postTargetTransformer(projectTarget, this.tree);
|
projectTarget = this.#postTargetTransformer(projectTarget, this.tree, {
|
||||||
|
projectName,
|
||||||
|
root: projectFromGraph.data.root,
|
||||||
|
});
|
||||||
|
|
||||||
if (
|
if (
|
||||||
projectTarget.options &&
|
projectTarget.options &&
|
||||||
@ -308,7 +312,7 @@ export async function migrateExecutorToPlugin<T>(
|
|||||||
createNodes: CreateNodes<T>,
|
createNodes: CreateNodes<T>,
|
||||||
specificProjectToMigrate?: string,
|
specificProjectToMigrate?: string,
|
||||||
skipTargetFilter?: SkipTargetFilter
|
skipTargetFilter?: SkipTargetFilter
|
||||||
): Promise<number> {
|
): Promise<Map<string, Set<string>>> {
|
||||||
const migrator = new ExecutorToPluginMigrator<T>(
|
const migrator = new ExecutorToPluginMigrator<T>(
|
||||||
tree,
|
tree,
|
||||||
projectGraph,
|
projectGraph,
|
||||||
@ -320,6 +324,5 @@ export async function migrateExecutorToPlugin<T>(
|
|||||||
specificProjectToMigrate,
|
specificProjectToMigrate,
|
||||||
skipTargetFilter
|
skipTargetFilter
|
||||||
);
|
);
|
||||||
const migratedProjectsAndTargets = await migrator.run();
|
return await migrator.run();
|
||||||
return migratedProjectsAndTargets.size;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,11 @@
|
|||||||
"factory": "./src/generators/convert-to-flat-config/generator",
|
"factory": "./src/generators/convert-to-flat-config/generator",
|
||||||
"schema": "./src/generators/convert-to-flat-config/schema.json",
|
"schema": "./src/generators/convert-to-flat-config/schema.json",
|
||||||
"description": "Convert an Nx workspace's ESLint configs to use Flat Config."
|
"description": "Convert an Nx workspace's ESLint configs to use Flat Config."
|
||||||
|
},
|
||||||
|
"convert-to-inferred": {
|
||||||
|
"factory": "./src/generators/convert-to-inferred/convert-to-inferred",
|
||||||
|
"schema": "./src/generators/convert-to-inferred/schema.json",
|
||||||
|
"description": "Convert existing ESLint project(s) using `@nx/eslint:lint` executor to use `@nx/eslint/plugin`."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,671 @@
|
|||||||
|
import {
|
||||||
|
getRelativeProjectJsonSchemaPath,
|
||||||
|
updateProjectConfiguration,
|
||||||
|
} from 'nx/src/generators/utils/project-configuration';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
|
import { convertToInferred } from './convert-to-inferred';
|
||||||
|
import {
|
||||||
|
addProjectConfiguration as _addProjectConfiguration,
|
||||||
|
type ExpandedPluginConfiguration,
|
||||||
|
joinPathFragments,
|
||||||
|
type ProjectConfiguration,
|
||||||
|
type ProjectGraph,
|
||||||
|
readNxJson,
|
||||||
|
readProjectConfiguration,
|
||||||
|
type Tree,
|
||||||
|
updateNxJson,
|
||||||
|
writeJson,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { TempFs } from '@nx/devkit/internal-testing-utils';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
|
||||||
|
let fs: TempFs;
|
||||||
|
|
||||||
|
let projectGraph: ProjectGraph;
|
||||||
|
jest.mock('@nx/devkit', () => ({
|
||||||
|
...jest.requireActual<any>('@nx/devkit'),
|
||||||
|
createProjectGraphAsync: jest.fn().mockImplementation(async () => {
|
||||||
|
return projectGraph;
|
||||||
|
}),
|
||||||
|
updateProjectConfiguration: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation((tree, projectName, projectConfiguration) => {
|
||||||
|
function handleEmptyTargets(
|
||||||
|
projectName: string,
|
||||||
|
projectConfiguration: ProjectConfiguration
|
||||||
|
): void {
|
||||||
|
if (
|
||||||
|
projectConfiguration.targets &&
|
||||||
|
!Object.keys(projectConfiguration.targets).length
|
||||||
|
) {
|
||||||
|
// Re-order `targets` to appear after the `// target` comment.
|
||||||
|
delete projectConfiguration.targets;
|
||||||
|
projectConfiguration[
|
||||||
|
'// targets'
|
||||||
|
] = `to see all targets run: nx show project ${projectName} --web`;
|
||||||
|
projectConfiguration.targets = {};
|
||||||
|
} else {
|
||||||
|
delete projectConfiguration['// targets'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectConfigFile = joinPathFragments(
|
||||||
|
projectConfiguration.root,
|
||||||
|
'project.json'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!tree.exists(projectConfigFile)) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot update Project ${projectName} at ${projectConfiguration.root}. It either doesn't exist yet, or may not use project.json for configuration. Use \`addProjectConfiguration()\` instead if you want to create a new project.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
handleEmptyTargets(projectName, projectConfiguration);
|
||||||
|
writeJson(tree, projectConfigFile, {
|
||||||
|
name: projectConfiguration.name ?? projectName,
|
||||||
|
$schema: getRelativeProjectJsonSchemaPath(tree, projectConfiguration),
|
||||||
|
...projectConfiguration,
|
||||||
|
root: undefined,
|
||||||
|
});
|
||||||
|
projectGraph.nodes[projectName].data = projectConfiguration;
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
function addProjectConfiguration(
|
||||||
|
tree: Tree,
|
||||||
|
name: string,
|
||||||
|
project: ProjectConfiguration
|
||||||
|
) {
|
||||||
|
_addProjectConfiguration(tree, name, project);
|
||||||
|
projectGraph.nodes[name] = {
|
||||||
|
name: name,
|
||||||
|
type: project.projectType === 'application' ? 'app' : 'lib',
|
||||||
|
data: {
|
||||||
|
projectType: project.projectType,
|
||||||
|
root: project.root,
|
||||||
|
targets: project.targets,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CreateEslintLintProjectOptions {
|
||||||
|
appName: string;
|
||||||
|
appRoot: string;
|
||||||
|
targetName: string;
|
||||||
|
legacyExecutor?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultCreateEslintLintProjectOptions: CreateEslintLintProjectOptions = {
|
||||||
|
appName: 'myapp',
|
||||||
|
appRoot: 'myapp',
|
||||||
|
targetName: 'lint',
|
||||||
|
legacyExecutor: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
function createTestProject(
|
||||||
|
tree: Tree,
|
||||||
|
opts: Partial<CreateEslintLintProjectOptions> = defaultCreateEslintLintProjectOptions
|
||||||
|
) {
|
||||||
|
let projectOpts = { ...defaultCreateEslintLintProjectOptions, ...opts };
|
||||||
|
const project: ProjectConfiguration = {
|
||||||
|
name: projectOpts.appName,
|
||||||
|
root: projectOpts.appRoot,
|
||||||
|
projectType: 'application',
|
||||||
|
targets: {
|
||||||
|
[projectOpts.targetName]: {
|
||||||
|
executor: projectOpts.legacyExecutor
|
||||||
|
? '@nrwl/linter:eslint'
|
||||||
|
: '@nx/eslint:lint',
|
||||||
|
options: {
|
||||||
|
eslintConfig: `${projectOpts.appRoot}/.eslintrc.json`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const eslintConfigContents = {
|
||||||
|
rules: {},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['./project.json'],
|
||||||
|
parser: 'jsonc-eslint-parser',
|
||||||
|
rules: {
|
||||||
|
'@nx/nx-plugin-checks': 'error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['./package.json'],
|
||||||
|
parser: 'jsonc-eslint-parser',
|
||||||
|
rules: {
|
||||||
|
'@nx/dependency-checks': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
buildTargets: ['build-base'],
|
||||||
|
ignoredDependencies: [
|
||||||
|
'nx',
|
||||||
|
'@nx/jest',
|
||||||
|
'typescript',
|
||||||
|
'eslint',
|
||||||
|
'@angular-devkit/core',
|
||||||
|
'@typescript-eslint/eslint-plugin',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
ignorePatterns: ['!**/*'],
|
||||||
|
};
|
||||||
|
const eslintConfigContentsAsString = JSON.stringify(eslintConfigContents);
|
||||||
|
|
||||||
|
tree.write(
|
||||||
|
`${projectOpts.appRoot}/.eslintrc.json`,
|
||||||
|
eslintConfigContentsAsString
|
||||||
|
);
|
||||||
|
fs.createFileSync(
|
||||||
|
`${projectOpts.appRoot}/.eslintrc.json`,
|
||||||
|
eslintConfigContentsAsString
|
||||||
|
);
|
||||||
|
|
||||||
|
tree.write(`${projectOpts.appRoot}/src/foo.ts`, `export const myValue = 2;`);
|
||||||
|
fs.createFileSync(
|
||||||
|
`${projectOpts.appRoot}/src/foo.ts`,
|
||||||
|
`export const myValue = 2;`
|
||||||
|
);
|
||||||
|
jest.doMock(
|
||||||
|
join(fs.tempDir, `${projectOpts.appRoot}/.eslintrc.json`),
|
||||||
|
() => ({
|
||||||
|
default: {
|
||||||
|
extends: '../../.eslintrc',
|
||||||
|
rules: {},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.ts'],
|
||||||
|
excludedFiles: ['./src/migrations/**'],
|
||||||
|
rules: {
|
||||||
|
'no-restricted-imports': ['error', '@nx/workspace'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'./package.json',
|
||||||
|
'./generators.json',
|
||||||
|
'./executors.json',
|
||||||
|
'./migrations.json',
|
||||||
|
],
|
||||||
|
parser: 'jsonc-eslint-parser',
|
||||||
|
rules: {
|
||||||
|
'@nx/nx-plugin-checks': 'error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['./package.json'],
|
||||||
|
parser: 'jsonc-eslint-parser',
|
||||||
|
rules: {
|
||||||
|
'@nx/dependency-checks': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
buildTargets: ['build-base'],
|
||||||
|
ignoredDependencies: [
|
||||||
|
'nx',
|
||||||
|
'@nx/jest',
|
||||||
|
'typescript',
|
||||||
|
'eslint',
|
||||||
|
'@angular-devkit/core',
|
||||||
|
'@typescript-eslint/eslint-plugin',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
ignorePatterns: ['!**/*'],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
virtual: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
addProjectConfiguration(tree, project.name, project);
|
||||||
|
fs.createFileSync(
|
||||||
|
`${projectOpts.appRoot}/project.json`,
|
||||||
|
JSON.stringify(project)
|
||||||
|
);
|
||||||
|
return project;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Eslint - Convert Executors To Plugin', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fs = new TempFs('eslint');
|
||||||
|
tree = createTreeWithEmptyWorkspace();
|
||||||
|
tree.root = fs.tempDir;
|
||||||
|
|
||||||
|
projectGraph = {
|
||||||
|
nodes: {},
|
||||||
|
dependencies: {},
|
||||||
|
externalNodes: {},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fs.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('--project', () => {
|
||||||
|
it('should setup a new Eslint plugin and only migrate one specific project', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const existingProject = createTestProject(tree, {
|
||||||
|
appRoot: 'existing',
|
||||||
|
appName: 'existing',
|
||||||
|
targetName: 'lint',
|
||||||
|
});
|
||||||
|
const project = createTestProject(tree, {
|
||||||
|
targetName: 'eslint',
|
||||||
|
});
|
||||||
|
const secondProject = createTestProject(tree, {
|
||||||
|
appRoot: 'second',
|
||||||
|
appName: 'second',
|
||||||
|
targetName: 'eslint',
|
||||||
|
});
|
||||||
|
const thirdProject = createTestProject(tree, {
|
||||||
|
appRoot: 'third',
|
||||||
|
appName: 'third',
|
||||||
|
targetName: 'linter',
|
||||||
|
});
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
nxJson.plugins ??= [];
|
||||||
|
nxJson.plugins.push({
|
||||||
|
plugin: '@nx/eslint/plugin',
|
||||||
|
options: {
|
||||||
|
targetName: 'lint',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
updateNxJson(tree, nxJson);
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await convertToInferred(tree, { project: 'myapp', skipFormat: true });
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
// project.json modifications
|
||||||
|
const updatedProject = readProjectConfiguration(tree, project.name);
|
||||||
|
const targetKeys = Object.keys(updatedProject.targets);
|
||||||
|
['lint'].forEach((key) => expect(targetKeys).not.toContain(key));
|
||||||
|
|
||||||
|
// nx.json modifications
|
||||||
|
const nxJsonPlugins = readNxJson(tree).plugins;
|
||||||
|
const addedTestEslintPlugin = nxJsonPlugins.find((plugin) => {
|
||||||
|
if (
|
||||||
|
typeof plugin !== 'string' &&
|
||||||
|
plugin.plugin === '@nx/eslint/plugin' &&
|
||||||
|
plugin.include?.length === 1
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(addedTestEslintPlugin).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
(addedTestEslintPlugin as ExpandedPluginConfiguration).include
|
||||||
|
).toEqual(['myapp/**/*']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add project to existing plugins includes', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const existingProject = createTestProject(tree, {
|
||||||
|
appRoot: 'existing',
|
||||||
|
appName: 'existing',
|
||||||
|
targetName: 'lint',
|
||||||
|
});
|
||||||
|
const project = createTestProject(tree, {
|
||||||
|
targetName: 'lint',
|
||||||
|
});
|
||||||
|
const secondProject = createTestProject(tree, {
|
||||||
|
appRoot: 'second',
|
||||||
|
appName: 'second',
|
||||||
|
targetName: 'lint',
|
||||||
|
});
|
||||||
|
const thirdProject = createTestProject(tree, {
|
||||||
|
appRoot: 'third',
|
||||||
|
appName: 'third',
|
||||||
|
targetName: 'lint',
|
||||||
|
});
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
nxJson.plugins ??= [];
|
||||||
|
nxJson.plugins.push({
|
||||||
|
plugin: '@nx/eslint/plugin',
|
||||||
|
include: ['existing/**/*'],
|
||||||
|
options: {
|
||||||
|
targetName: 'lint',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
updateNxJson(tree, nxJson);
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await convertToInferred(tree, { project: 'myapp', skipFormat: true });
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
// project.json modifications
|
||||||
|
const updatedProject = readProjectConfiguration(tree, project.name);
|
||||||
|
const targetKeys = Object.keys(updatedProject.targets);
|
||||||
|
expect(targetKeys).not.toContain('lint');
|
||||||
|
|
||||||
|
// nx.json modifications
|
||||||
|
const nxJsonPlugins = readNxJson(tree).plugins;
|
||||||
|
const addedTestEslintPlugin = nxJsonPlugins.find((plugin) => {
|
||||||
|
if (
|
||||||
|
typeof plugin !== 'string' &&
|
||||||
|
plugin.plugin === '@nx/eslint/plugin' &&
|
||||||
|
plugin.include?.length === 2
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(addedTestEslintPlugin).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
(addedTestEslintPlugin as ExpandedPluginConfiguration).include
|
||||||
|
).toEqual(['existing/**/*', 'myapp/**/*']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove include when all projects are included', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const existingProject = createTestProject(tree, {
|
||||||
|
appRoot: 'existing',
|
||||||
|
appName: 'existing',
|
||||||
|
targetName: 'lint',
|
||||||
|
});
|
||||||
|
const project = createTestProject(tree, {
|
||||||
|
targetName: 'lint',
|
||||||
|
});
|
||||||
|
const secondProject = createTestProject(tree, {
|
||||||
|
appRoot: 'second',
|
||||||
|
appName: 'second',
|
||||||
|
targetName: 'lint',
|
||||||
|
});
|
||||||
|
const thirdProject = createTestProject(tree, {
|
||||||
|
appRoot: 'third',
|
||||||
|
appName: 'third',
|
||||||
|
targetName: 'lint',
|
||||||
|
});
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
nxJson.plugins ??= [];
|
||||||
|
nxJson.plugins.push({
|
||||||
|
plugin: '@nx/eslint/plugin',
|
||||||
|
include: ['existing/**/*', 'second/**/*', 'third/**/*'],
|
||||||
|
options: {
|
||||||
|
targetName: 'lint',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
updateNxJson(tree, nxJson);
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await convertToInferred(tree, { project: 'myapp', skipFormat: true });
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
// project.json modifications
|
||||||
|
const updatedProject = readProjectConfiguration(tree, project.name);
|
||||||
|
const targetKeys = Object.keys(updatedProject.targets);
|
||||||
|
['lint'].forEach((key) => expect(targetKeys).not.toContain(key));
|
||||||
|
|
||||||
|
// nx.json modifications
|
||||||
|
const nxJsonPlugins = readNxJson(tree).plugins;
|
||||||
|
const addedTestEslintPlugin = nxJsonPlugins.find((plugin) => {
|
||||||
|
if (
|
||||||
|
typeof plugin !== 'string' &&
|
||||||
|
plugin.plugin === '@nx/eslint/plugin' &&
|
||||||
|
!plugin.include
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(addedTestEslintPlugin).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
(addedTestEslintPlugin as ExpandedPluginConfiguration).include
|
||||||
|
).not.toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('--all', () => {
|
||||||
|
it('should successfully migrate a project using Eslint executors to plugin', async () => {
|
||||||
|
const project = createTestProject(tree);
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await convertToInferred(tree, { skipFormat: true });
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
// project.json modifications
|
||||||
|
const updatedProject = readProjectConfiguration(tree, project.name);
|
||||||
|
const targetKeys = Object.keys(updatedProject.targets);
|
||||||
|
expect(targetKeys).not.toContain('lint');
|
||||||
|
|
||||||
|
// nx.json modifications
|
||||||
|
const nxJsonPlugins = readNxJson(tree).plugins;
|
||||||
|
const hasEslintPlugin = nxJsonPlugins.find((plugin) =>
|
||||||
|
typeof plugin === 'string'
|
||||||
|
? plugin === '@nx/eslint/plugin'
|
||||||
|
: plugin.plugin === '@nx/eslint/plugin'
|
||||||
|
);
|
||||||
|
expect(hasEslintPlugin).toBeTruthy();
|
||||||
|
if (typeof hasEslintPlugin !== 'string') {
|
||||||
|
[['targetName', 'lint']].forEach(([targetOptionName, targetName]) => {
|
||||||
|
expect(hasEslintPlugin.options[targetOptionName]).toEqual(targetName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should setup Eslint plugin to match projects', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const project = createTestProject(tree, {
|
||||||
|
targetName: 'eslint',
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await convertToInferred(tree, { skipFormat: true });
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
// project.json modifications
|
||||||
|
const updatedProject = readProjectConfiguration(tree, project.name);
|
||||||
|
const targetKeys = Object.keys(updatedProject.targets);
|
||||||
|
['eslint'].forEach((key) => expect(targetKeys).not.toContain(key));
|
||||||
|
|
||||||
|
// nx.json modifications
|
||||||
|
const nxJsonPlugins = readNxJson(tree).plugins;
|
||||||
|
const hasEslintPlugin = nxJsonPlugins.find((plugin) =>
|
||||||
|
typeof plugin === 'string'
|
||||||
|
? plugin === '@nx/eslint/plugin'
|
||||||
|
: plugin.plugin === '@nx/eslint/plugin'
|
||||||
|
);
|
||||||
|
expect(hasEslintPlugin).toBeTruthy();
|
||||||
|
if (typeof hasEslintPlugin !== 'string') {
|
||||||
|
[['targetName', 'eslint']].forEach(([targetOptionName, targetName]) => {
|
||||||
|
expect(hasEslintPlugin.options[targetOptionName]).toEqual(targetName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle targets using legacy executor', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const project = createTestProject(tree, {
|
||||||
|
targetName: 'eslint',
|
||||||
|
legacyExecutor: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await convertToInferred(tree, { skipFormat: true });
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
// project.json modifications
|
||||||
|
const updatedProject = readProjectConfiguration(tree, project.name);
|
||||||
|
const targetKeys = Object.keys(updatedProject.targets);
|
||||||
|
expect(targetKeys).not.toContain('eslint');
|
||||||
|
|
||||||
|
// nx.json modifications
|
||||||
|
const nxJsonPlugins = readNxJson(tree).plugins;
|
||||||
|
const hasEslintPlugin = nxJsonPlugins.find((plugin) =>
|
||||||
|
typeof plugin === 'string'
|
||||||
|
? plugin === '@nx/eslint/plugin'
|
||||||
|
: plugin.plugin === '@nx/eslint/plugin'
|
||||||
|
);
|
||||||
|
expect(hasEslintPlugin).toBeTruthy();
|
||||||
|
if (typeof hasEslintPlugin !== 'string') {
|
||||||
|
[['targetName', 'eslint']].forEach(([targetOptionName, targetName]) => {
|
||||||
|
expect(hasEslintPlugin.options[targetOptionName]).toEqual(targetName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should setup a new Eslint plugin to match only projects migrated', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const existingProject = createTestProject(tree, {
|
||||||
|
appRoot: 'existing',
|
||||||
|
appName: 'existing',
|
||||||
|
targetName: 'lint',
|
||||||
|
});
|
||||||
|
const project = createTestProject(tree, {
|
||||||
|
targetName: 'eslint',
|
||||||
|
});
|
||||||
|
const secondProject = createTestProject(tree, {
|
||||||
|
appRoot: 'second',
|
||||||
|
appName: 'second',
|
||||||
|
targetName: 'eslint',
|
||||||
|
});
|
||||||
|
const thirdProject = createTestProject(tree, {
|
||||||
|
appRoot: 'third',
|
||||||
|
appName: 'third',
|
||||||
|
targetName: 'linter',
|
||||||
|
});
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
nxJson.plugins ??= [];
|
||||||
|
nxJson.plugins.push({
|
||||||
|
plugin: '@nx/eslint/plugin',
|
||||||
|
options: {
|
||||||
|
targetName: 'lint',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
updateNxJson(tree, nxJson);
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await convertToInferred(tree, { skipFormat: true });
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
// project.json modifications
|
||||||
|
const updatedProject = readProjectConfiguration(tree, project.name);
|
||||||
|
const targetKeys = Object.keys(updatedProject.targets);
|
||||||
|
expect(targetKeys).not.toContain('eslint');
|
||||||
|
|
||||||
|
// nx.json modifications
|
||||||
|
const nxJsonPlugins = readNxJson(tree).plugins;
|
||||||
|
const addedLintEslintPlugin = nxJsonPlugins.find((plugin) => {
|
||||||
|
if (
|
||||||
|
typeof plugin !== 'string' &&
|
||||||
|
plugin.plugin === '@nx/eslint/plugin' &&
|
||||||
|
plugin.include?.length === 2
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(addedLintEslintPlugin).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
(addedLintEslintPlugin as ExpandedPluginConfiguration).include
|
||||||
|
).toEqual(['myapp/**/*', 'second/**/*']);
|
||||||
|
|
||||||
|
const addedLinterEslintPlugin = nxJsonPlugins.find((plugin) => {
|
||||||
|
if (
|
||||||
|
typeof plugin !== 'string' &&
|
||||||
|
plugin.plugin === '@nx/eslint/plugin' &&
|
||||||
|
plugin.include?.length === 1
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(addedLinterEslintPlugin).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
(addedLinterEslintPlugin as ExpandedPluginConfiguration).include
|
||||||
|
).toEqual(['third/**/*']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should keep Eslint options in project.json', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const project = createTestProject(tree);
|
||||||
|
project.targets.lint.options.cacheLocation = 'cache-dir';
|
||||||
|
updateProjectConfiguration(tree, project.name, project);
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await convertToInferred(tree, { skipFormat: true });
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
// project.json modifications
|
||||||
|
const updatedProject = readProjectConfiguration(tree, project.name);
|
||||||
|
expect(updatedProject.targets.lint).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"options": {
|
||||||
|
"cache-location": "cache-dir",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
// nx.json modifications
|
||||||
|
const nxJsonPlugins = readNxJson(tree).plugins;
|
||||||
|
const hasEslintPlugin = nxJsonPlugins.find((plugin) =>
|
||||||
|
typeof plugin === 'string'
|
||||||
|
? plugin === '@nx/eslint/plugin'
|
||||||
|
: plugin.plugin === '@nx/eslint/plugin'
|
||||||
|
);
|
||||||
|
expect(hasEslintPlugin).toBeTruthy();
|
||||||
|
if (typeof hasEslintPlugin !== 'string') {
|
||||||
|
[['targetName', 'lint']].forEach(([targetOptionName, targetName]) => {
|
||||||
|
expect(hasEslintPlugin.options[targetOptionName]).toEqual(targetName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add Eslint options found in targetDefaults for the executor to the project.json', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
nxJson.targetDefaults ??= {};
|
||||||
|
nxJson.targetDefaults['@nx/eslint:lint'] = {
|
||||||
|
options: {
|
||||||
|
maxWarnings: 10,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
updateNxJson(tree, nxJson);
|
||||||
|
const project = createTestProject(tree);
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
await convertToInferred(tree, { skipFormat: true });
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
// project.json modifications
|
||||||
|
const updatedProject = readProjectConfiguration(tree, project.name);
|
||||||
|
expect(updatedProject.targets.lint).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"options": {
|
||||||
|
"max-warnings": 10,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
// nx.json modifications
|
||||||
|
const nxJsonPlugins = readNxJson(tree).plugins;
|
||||||
|
const hasEslintPlugin = nxJsonPlugins.find((plugin) =>
|
||||||
|
typeof plugin === 'string'
|
||||||
|
? plugin === '@nx/eslint/plugin'
|
||||||
|
: plugin.plugin === '@nx/eslint/plugin'
|
||||||
|
);
|
||||||
|
expect(hasEslintPlugin).toBeTruthy();
|
||||||
|
if (typeof hasEslintPlugin !== 'string') {
|
||||||
|
[['targetName', 'lint']].forEach(([targetOptionName, targetName]) => {
|
||||||
|
expect(hasEslintPlugin.options[targetOptionName]).toEqual(targetName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,137 @@
|
|||||||
|
import {
|
||||||
|
createProjectGraphAsync,
|
||||||
|
formatFiles,
|
||||||
|
names,
|
||||||
|
type TargetConfiguration,
|
||||||
|
type Tree,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { createNodes, EslintPluginOptions } from '../../plugins/plugin';
|
||||||
|
import { migrateExecutorToPlugin } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator';
|
||||||
|
import { targetOptionsToCliMap } from './lib/target-options-map';
|
||||||
|
import { interpolate } from 'nx/src/tasks-runner/utils';
|
||||||
|
|
||||||
|
interface Schema {
|
||||||
|
project?: string;
|
||||||
|
skipFormat?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function convertToInferred(tree: Tree, options: Schema) {
|
||||||
|
const projectGraph = await createProjectGraphAsync();
|
||||||
|
|
||||||
|
const migratedProjectsModern =
|
||||||
|
await migrateExecutorToPlugin<EslintPluginOptions>(
|
||||||
|
tree,
|
||||||
|
projectGraph,
|
||||||
|
'@nx/eslint:lint',
|
||||||
|
'@nx/eslint/plugin',
|
||||||
|
(targetName) => ({ targetName }),
|
||||||
|
postTargetTransformer,
|
||||||
|
createNodes,
|
||||||
|
options.project
|
||||||
|
);
|
||||||
|
|
||||||
|
const migratedProjectsLegacy =
|
||||||
|
await migrateExecutorToPlugin<EslintPluginOptions>(
|
||||||
|
tree,
|
||||||
|
projectGraph,
|
||||||
|
'@nrwl/linter:eslint',
|
||||||
|
'@nx/eslint/plugin',
|
||||||
|
(targetName) => ({ targetName }),
|
||||||
|
postTargetTransformer,
|
||||||
|
createNodes,
|
||||||
|
options.project
|
||||||
|
);
|
||||||
|
|
||||||
|
const migratedProjects =
|
||||||
|
migratedProjectsModern.size + migratedProjectsLegacy.size;
|
||||||
|
if (migratedProjects === 0) {
|
||||||
|
throw new Error('Could not find any targets to migrate.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.skipFormat) {
|
||||||
|
await formatFiles(tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function postTargetTransformer(
|
||||||
|
target: TargetConfiguration,
|
||||||
|
tree: Tree,
|
||||||
|
projectDetails: { projectName: string; root: string }
|
||||||
|
): TargetConfiguration {
|
||||||
|
if (target.inputs) {
|
||||||
|
target.inputs = target.inputs.filter(
|
||||||
|
(input) =>
|
||||||
|
typeof input === 'string' &&
|
||||||
|
![
|
||||||
|
'default',
|
||||||
|
'{workspaceRoot}/.eslintrc.json',
|
||||||
|
'{workspaceRoot}/.eslintignore',
|
||||||
|
'{workspaceRoot}/eslint.config.js',
|
||||||
|
].includes(input)
|
||||||
|
);
|
||||||
|
if (target.inputs.length === 0) {
|
||||||
|
delete target.inputs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.options) {
|
||||||
|
if ('eslintConfig' in target.options) {
|
||||||
|
delete target.options.eslintConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('force' in target.options) {
|
||||||
|
delete target.options.force;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('silent' in target.options) {
|
||||||
|
delete target.options.silent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('hasTypeAwareRules' in target.options) {
|
||||||
|
delete target.options.hasTypeAwareRules;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('errorOnUnmatchedPattern' in target.options) {
|
||||||
|
if (!target.options.errorOnUnmatchedPattern) {
|
||||||
|
target.options['no-error-on-unmatched-pattern'] = true;
|
||||||
|
}
|
||||||
|
delete target.options.errorOnUnmatchedPattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('outputFile' in target.options) {
|
||||||
|
target.outputs ??= [];
|
||||||
|
target.outputs.push(target.options.outputFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key in targetOptionsToCliMap) {
|
||||||
|
if (target.options[key]) {
|
||||||
|
target.options[targetOptionsToCliMap[key]] = target.options[key];
|
||||||
|
delete target.options[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('lintFilePatterns' in target.options) {
|
||||||
|
const normalizedLintFilePatterns = target.options.lintFilePatterns.map(
|
||||||
|
(pattern) => {
|
||||||
|
return interpolate(pattern, {
|
||||||
|
workspaceRoot: '',
|
||||||
|
projectRoot: projectDetails.root,
|
||||||
|
projectName: projectDetails.projectName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
target.options.args = normalizedLintFilePatterns.map((pattern) =>
|
||||||
|
pattern.startsWith(projectDetails.root)
|
||||||
|
? pattern.replace(new RegExp(`^${projectDetails.root}/`), './')
|
||||||
|
: pattern
|
||||||
|
);
|
||||||
|
|
||||||
|
delete target.options.lintFilePatterns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default convertToInferred;
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
export const targetOptionsToCliMap = {
|
||||||
|
fix: 'fix',
|
||||||
|
format: 'format',
|
||||||
|
cache: 'cache',
|
||||||
|
cacheLocation: 'cache-location',
|
||||||
|
cacheStrategy: 'cache-strategy',
|
||||||
|
noEslintrc: 'no-eslintrc',
|
||||||
|
outputFile: 'output-file',
|
||||||
|
maxWarnings: 'max-warnings',
|
||||||
|
quiet: 'quiet',
|
||||||
|
ignorePath: 'ignore-path',
|
||||||
|
rulesdir: 'rulesdir',
|
||||||
|
resolvePluginsRelativeTo: 'resolve-plugins-relative-to',
|
||||||
|
reportUnusedDisableDirectives: 'report-unused-disable-directives',
|
||||||
|
printConfig: 'print-config',
|
||||||
|
};
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/schema",
|
||||||
|
"$id": "NxEslintConvertToInferred",
|
||||||
|
"description": "Convert existing Eslint project(s) using `@nx/eslint:lint` executor to use `@nx/eslint/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target.",
|
||||||
|
"title": "Convert Eslint project from executor to plugin",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"project": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The project to convert from using the `@nx/eslint:lint` executor to use `@nx/eslint/plugin`.",
|
||||||
|
"x-priority": "important"
|
||||||
|
},
|
||||||
|
"skipFormat": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether to format files at the end of the migration.",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -28,7 +28,7 @@ export async function convertToInferred(tree: Tree, options: Schema) {
|
|||||||
options.project
|
options.project
|
||||||
);
|
);
|
||||||
|
|
||||||
if (migratedProjects === 0) {
|
if (migratedProjects.size === 0) {
|
||||||
throw new Error('Could not find any targets to migrate.');
|
throw new Error('Could not find any targets to migrate.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user