chore(devkit): add util for migrating to a plugin (#19942)
This commit is contained in:
parent
b7fc7192cf
commit
bdde0ddc98
@ -0,0 +1,447 @@
|
||||
import { Tree } from 'nx/src/generators/tree';
|
||||
import { CreateNodes } from 'nx/src/utils/nx-plugin';
|
||||
import { createTreeWithEmptyWorkspace } from 'nx/src/generators/testing-utils/create-tree-with-empty-workspace';
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
readProjectConfiguration,
|
||||
} from 'nx/src/generators/utils/project-configuration';
|
||||
|
||||
import { replaceProjectConfigurationsWithPlugin } from './replace-project-configuration-with-plugin';
|
||||
|
||||
describe('replaceProjectConfigurationsWithPlugin', () => {
|
||||
let tree: Tree;
|
||||
let createNodes: CreateNodes;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
tree.write('proj/file.txt', '');
|
||||
createNodes = [
|
||||
'proj/file.txt',
|
||||
() => ({
|
||||
projects: {
|
||||
proj: {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'nx:run-commands',
|
||||
dependsOn: ['^build-base'],
|
||||
inputs: ['default', '^default'],
|
||||
outputs: ['{options.output}', '{projectRoot}/outputs'],
|
||||
options: {
|
||||
configFile: 'file.txt',
|
||||
},
|
||||
configurations: {
|
||||
production: {
|
||||
configFile: 'file.prod.txt',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
];
|
||||
});
|
||||
|
||||
it('should not update the target when it uses a different executor', async () => {
|
||||
const buildTarget = {
|
||||
executor: 'nx:run-script',
|
||||
inputs: ['default', '^default'],
|
||||
outputs: ['{options.output}', '{projectRoot}/outputs'],
|
||||
options: {
|
||||
configFile: 'file.txt',
|
||||
},
|
||||
};
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
build: buildTarget,
|
||||
},
|
||||
});
|
||||
|
||||
replaceProjectConfigurationsWithPlugin(
|
||||
tree,
|
||||
new Map([['proj', 'proj']]),
|
||||
'plugin-path',
|
||||
createNodes,
|
||||
{}
|
||||
);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual(
|
||||
buildTarget
|
||||
);
|
||||
});
|
||||
|
||||
describe('options', () => {
|
||||
it('should be removed when there are no other options', async () => {
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'nx:run-commands',
|
||||
inputs: ['default', '^default'],
|
||||
outputs: ['{options.output}', '{projectRoot}/outputs'],
|
||||
options: {
|
||||
configFile: 'file.txt',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
replaceProjectConfigurationsWithPlugin(
|
||||
tree,
|
||||
new Map([['proj', 'proj']]),
|
||||
'plugin-path',
|
||||
createNodes,
|
||||
{}
|
||||
);
|
||||
|
||||
expect(
|
||||
readProjectConfiguration(tree, 'proj').targets.build
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should not be removed when there are other options', async () => {
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'nx:run-commands',
|
||||
inputs: ['default', '^default'],
|
||||
outputs: ['{options.output}', '{projectRoot}/outputs'],
|
||||
options: {
|
||||
configFile: 'file.txt',
|
||||
watch: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
replaceProjectConfigurationsWithPlugin(
|
||||
tree,
|
||||
new Map([['proj', 'proj']]),
|
||||
'plugin-path',
|
||||
createNodes,
|
||||
{}
|
||||
);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({
|
||||
options: {
|
||||
watch: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('inputs', () => {
|
||||
it('should not be removed if there are additional inputs', () => {
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'nx:run-commands',
|
||||
inputs: ['default', '^default', '{workspaceRoot}/file.txt'],
|
||||
outputs: ['{options.output}', '{projectRoot}/outputs'],
|
||||
options: {
|
||||
configFile: 'file.txt',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
replaceProjectConfigurationsWithPlugin(
|
||||
tree,
|
||||
new Map([['proj', 'proj']]),
|
||||
'plugin-path',
|
||||
createNodes,
|
||||
{}
|
||||
);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({
|
||||
inputs: ['default', '^default', '{workspaceRoot}/file.txt'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should not be removed if there are additional inputs which are objects', () => {
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'nx:run-commands',
|
||||
inputs: [
|
||||
'default',
|
||||
'^default',
|
||||
{
|
||||
env: 'HOME',
|
||||
},
|
||||
],
|
||||
outputs: ['{options.output}', '{projectRoot}/outputs'],
|
||||
options: {
|
||||
configFile: 'file.txt',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
replaceProjectConfigurationsWithPlugin(
|
||||
tree,
|
||||
new Map([['proj', 'proj']]),
|
||||
'plugin-path',
|
||||
createNodes,
|
||||
{}
|
||||
);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({
|
||||
inputs: [
|
||||
'default',
|
||||
'^default',
|
||||
{
|
||||
env: 'HOME',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should not be removed if there are less inputs', () => {
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'nx:run-commands',
|
||||
inputs: ['default'],
|
||||
outputs: ['{options.output}', '{projectRoot}/outputs'],
|
||||
options: {
|
||||
configFile: 'file.txt',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
replaceProjectConfigurationsWithPlugin(
|
||||
tree,
|
||||
new Map([['proj', 'proj']]),
|
||||
'plugin-path',
|
||||
createNodes,
|
||||
{}
|
||||
);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({
|
||||
inputs: ['default'],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('outputs', () => {
|
||||
it('should not be removed if there are additional outputs', () => {
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'nx:run-commands',
|
||||
inputs: ['default', '^default'],
|
||||
outputs: [
|
||||
'{options.output}',
|
||||
'{projectRoot}/outputs',
|
||||
'{projectRoot}/more-outputs',
|
||||
],
|
||||
options: {
|
||||
configFile: 'file.txt',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
replaceProjectConfigurationsWithPlugin(
|
||||
tree,
|
||||
new Map([['proj', 'proj']]),
|
||||
'plugin-path',
|
||||
createNodes,
|
||||
{}
|
||||
);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({
|
||||
outputs: [
|
||||
'{options.output}',
|
||||
'{projectRoot}/outputs',
|
||||
'{projectRoot}/more-outputs',
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should not be removed if there are less outputs', () => {
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'nx:run-commands',
|
||||
outputs: ['{options.output}'],
|
||||
options: {
|
||||
configFile: 'file.txt',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
replaceProjectConfigurationsWithPlugin(
|
||||
tree,
|
||||
new Map([['proj', 'proj']]),
|
||||
'plugin-path',
|
||||
createNodes,
|
||||
{}
|
||||
);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({
|
||||
outputs: ['{options.output}'],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('dependsOn', () => {
|
||||
it('should be removed when it is the same', () => {
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'nx:run-commands',
|
||||
dependsOn: ['^build-base'],
|
||||
options: {
|
||||
configFile: 'file.txt',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
replaceProjectConfigurationsWithPlugin(
|
||||
tree,
|
||||
new Map([['proj', 'proj']]),
|
||||
'plugin-path',
|
||||
createNodes,
|
||||
{}
|
||||
);
|
||||
|
||||
expect(
|
||||
readProjectConfiguration(tree, 'proj').targets.build
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should not be removed when there are more dependent tasks', () => {
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'nx:run-commands',
|
||||
dependsOn: ['^build-base', 'prebuild'],
|
||||
options: {
|
||||
configFile: 'file.txt',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
replaceProjectConfigurationsWithPlugin(
|
||||
tree,
|
||||
new Map([['proj', 'proj']]),
|
||||
'plugin-path',
|
||||
createNodes,
|
||||
{}
|
||||
);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({
|
||||
dependsOn: ['^build-base', 'prebuild'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should not be removed when there are less dependent tasks', () => {
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'nx:run-commands',
|
||||
dependsOn: [],
|
||||
options: {
|
||||
configFile: 'file.txt',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
replaceProjectConfigurationsWithPlugin(
|
||||
tree,
|
||||
new Map([['proj', 'proj']]),
|
||||
'plugin-path',
|
||||
createNodes,
|
||||
{}
|
||||
);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({
|
||||
dependsOn: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('defaultConfiguration', () => {
|
||||
it('should not be removed when the defaultConfiguration is different', () => {
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'nx:run-commands',
|
||||
options: {
|
||||
configFile: 'file.txt',
|
||||
},
|
||||
defaultConfiguration: 'other',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
replaceProjectConfigurationsWithPlugin(
|
||||
tree,
|
||||
new Map([['proj', 'proj']]),
|
||||
'plugin-path',
|
||||
createNodes,
|
||||
{}
|
||||
);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({
|
||||
defaultConfiguration: 'other',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('configurations', () => {
|
||||
it('should not be removed when an additional configuration is defined', () => {
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
build: {
|
||||
executor: 'nx:run-commands',
|
||||
options: {
|
||||
configFile: 'file.txt',
|
||||
},
|
||||
configurations: {
|
||||
other: {
|
||||
configFile: 'other-file.txt',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
replaceProjectConfigurationsWithPlugin(
|
||||
tree,
|
||||
new Map([['proj', 'proj']]),
|
||||
'plugin-path',
|
||||
createNodes,
|
||||
{}
|
||||
);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj').targets.build).toEqual({
|
||||
configurations: {
|
||||
other: {
|
||||
configFile: 'other-file.txt',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,206 @@
|
||||
import type {
|
||||
ProjectConfiguration,
|
||||
TargetConfiguration,
|
||||
} from 'nx/src/config/workspace-json-project-json';
|
||||
import type { Tree } from 'nx/src/generators/tree';
|
||||
import type { CreateNodes } from 'nx/src/utils/nx-plugin';
|
||||
import { requireNx } from '../../nx';
|
||||
const {
|
||||
readNxJson,
|
||||
updateNxJson,
|
||||
glob,
|
||||
hashObject,
|
||||
findProjectForPath,
|
||||
readProjectConfiguration,
|
||||
updateProjectConfiguration,
|
||||
} = requireNx();
|
||||
|
||||
export function replaceProjectConfigurationsWithPlugin<T = unknown>(
|
||||
tree: Tree,
|
||||
rootMappings: Map<string, string>,
|
||||
pluginPath: string,
|
||||
createNodes: CreateNodes<T>,
|
||||
pluginOptions: T
|
||||
): void {
|
||||
const nxJson = readNxJson(tree);
|
||||
const hasPlugin = nxJson.plugins?.some((p) =>
|
||||
typeof p === 'string' ? p === pluginPath : p.plugin === pluginPath
|
||||
);
|
||||
|
||||
if (hasPlugin) {
|
||||
return;
|
||||
}
|
||||
|
||||
nxJson.plugins ??= [];
|
||||
nxJson.plugins.push({
|
||||
plugin: pluginPath,
|
||||
options: pluginOptions,
|
||||
});
|
||||
updateNxJson(tree, nxJson);
|
||||
|
||||
const [pluginGlob, createNodesFunction] = createNodes;
|
||||
const configFiles = glob(tree, [pluginGlob]);
|
||||
|
||||
for (const configFile of configFiles) {
|
||||
try {
|
||||
const projectName = findProjectForPath(configFile, rootMappings);
|
||||
const projectConfig = readProjectConfiguration(tree, projectName);
|
||||
const nodes = createNodesFunction(configFile, pluginOptions, {
|
||||
workspaceRoot: tree.root,
|
||||
nxJsonConfiguration: readNxJson(tree),
|
||||
});
|
||||
const node = nodes.projects[Object.keys(nodes.projects)[0]];
|
||||
|
||||
for (const [targetName, targetConfig] of Object.entries(node.targets)) {
|
||||
const targetFromProjectConfig = projectConfig.targets[targetName];
|
||||
|
||||
if (targetFromProjectConfig.executor !== targetConfig.executor) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const targetFromCreateNodes = node.targets[targetName];
|
||||
|
||||
removeConfigurationDefinedByPlugin(
|
||||
targetName,
|
||||
targetFromProjectConfig,
|
||||
targetFromCreateNodes,
|
||||
projectConfig
|
||||
);
|
||||
}
|
||||
|
||||
updateProjectConfiguration(tree, projectName, projectConfig);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeConfigurationDefinedByPlugin<T>(
|
||||
targetName: string,
|
||||
targetFromProjectConfig: TargetConfiguration<T>,
|
||||
targetFromCreateNodes: TargetConfiguration<T>,
|
||||
projectConfig: ProjectConfiguration
|
||||
) {
|
||||
// Executor
|
||||
delete targetFromProjectConfig.executor;
|
||||
|
||||
// Default Configuration
|
||||
if (
|
||||
targetFromProjectConfig.defaultConfiguration ===
|
||||
targetFromCreateNodes.defaultConfiguration
|
||||
) {
|
||||
delete targetFromProjectConfig.defaultConfiguration;
|
||||
}
|
||||
|
||||
// Cache
|
||||
if (targetFromProjectConfig.cache === targetFromCreateNodes.cache) {
|
||||
delete targetFromProjectConfig.cache;
|
||||
}
|
||||
|
||||
// Depends On
|
||||
if (
|
||||
targetFromProjectConfig.dependsOn &&
|
||||
shouldRemoveArrayProperty(
|
||||
targetFromProjectConfig.dependsOn,
|
||||
targetFromCreateNodes.dependsOn
|
||||
)
|
||||
) {
|
||||
delete targetFromProjectConfig.dependsOn;
|
||||
}
|
||||
|
||||
// Outputs
|
||||
if (
|
||||
targetFromProjectConfig.outputs &&
|
||||
shouldRemoveArrayProperty(
|
||||
targetFromProjectConfig.outputs,
|
||||
targetFromCreateNodes.outputs
|
||||
)
|
||||
) {
|
||||
delete targetFromProjectConfig.outputs;
|
||||
}
|
||||
|
||||
// Inputs
|
||||
if (
|
||||
targetFromProjectConfig.inputs &&
|
||||
shouldRemoveArrayProperty(
|
||||
targetFromProjectConfig.inputs,
|
||||
targetFromCreateNodes.inputs
|
||||
)
|
||||
) {
|
||||
delete targetFromProjectConfig.inputs;
|
||||
}
|
||||
|
||||
// Options
|
||||
for (const [optionName, optionValue] of Object.entries(
|
||||
targetFromProjectConfig.options ?? {}
|
||||
)) {
|
||||
if (targetFromCreateNodes.options[optionName] === optionValue) {
|
||||
delete targetFromProjectConfig.options[optionName];
|
||||
}
|
||||
}
|
||||
if (Object.keys(targetFromProjectConfig.options).length === 0) {
|
||||
delete targetFromProjectConfig.options;
|
||||
}
|
||||
|
||||
// Configurations
|
||||
for (const [configName, configOptions] of Object.entries(
|
||||
targetFromProjectConfig.configurations ?? {}
|
||||
)) {
|
||||
for (const [optionName, optionValue] of Object.entries(configOptions)) {
|
||||
if (
|
||||
targetFromCreateNodes.configurations?.[configName]?.[optionName] ===
|
||||
optionValue
|
||||
) {
|
||||
delete targetFromProjectConfig.configurations[configName][optionName];
|
||||
}
|
||||
}
|
||||
if (Object.keys(configOptions).length === 0) {
|
||||
delete targetFromProjectConfig.configurations[configName];
|
||||
}
|
||||
}
|
||||
if (Object.keys(targetFromProjectConfig.configurations ?? {}).length === 0) {
|
||||
delete targetFromProjectConfig.configurations;
|
||||
}
|
||||
|
||||
if (Object.keys(targetFromProjectConfig).length === 0) {
|
||||
delete projectConfig.targets[targetName];
|
||||
}
|
||||
}
|
||||
|
||||
function shouldRemoveArrayProperty(
|
||||
arrayValuesFromProjectConfiguration: (object | string)[],
|
||||
arrayValuesFromCreateNodes: (object | string)[]
|
||||
) {
|
||||
const setOfArrayValuesFromProjectConfiguration = new Set(
|
||||
arrayValuesFromProjectConfiguration
|
||||
);
|
||||
loopThroughArrayValuesFromCreateNodes: for (const arrayValueFromCreateNodes of arrayValuesFromCreateNodes) {
|
||||
if (typeof arrayValueFromCreateNodes === 'string') {
|
||||
if (
|
||||
!setOfArrayValuesFromProjectConfiguration.has(arrayValueFromCreateNodes)
|
||||
) {
|
||||
// If the inputs from the project configuration is missing an input from createNodes it was removed
|
||||
return false;
|
||||
} else {
|
||||
setOfArrayValuesFromProjectConfiguration.delete(
|
||||
arrayValueFromCreateNodes
|
||||
);
|
||||
}
|
||||
} else {
|
||||
for (const arrayValue of setOfArrayValuesFromProjectConfiguration.values()) {
|
||||
if (
|
||||
typeof arrayValue !== 'string' &&
|
||||
hashObject(arrayValue) === hashObject(arrayValueFromCreateNodes)
|
||||
) {
|
||||
setOfArrayValuesFromProjectConfiguration.delete(arrayValue);
|
||||
// Continue the outer loop, breaking out of this loop
|
||||
continue loopThroughArrayValuesFromCreateNodes;
|
||||
}
|
||||
}
|
||||
// If an input was not matched, that means the input was removed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// If there are still inputs in the project configuration, they have added additional inputs
|
||||
return setOfArrayValuesFromProjectConfiguration.size === 0;
|
||||
}
|
||||
@ -14,6 +14,7 @@ export { sortObjectByKeys } from './utils/object-sort';
|
||||
export { stripIndent } from './utils/logger';
|
||||
export { readModulePackageJson } from './utils/package-json';
|
||||
export { splitByColons } from './utils/split-target';
|
||||
export { hashObject } from './hasher/file-hasher';
|
||||
export {
|
||||
createProjectRootMappingsFromProjectConfigurations,
|
||||
findProjectForPath,
|
||||
|
||||
@ -6,7 +6,7 @@ exports[`create-nodes-plugin/generator generator should run successfully 1`] = `
|
||||
CreateNodesContext,
|
||||
TargetConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { basename, dirname, extname, join, resolve } from "path";
|
||||
import { basename, dirname, extname, join, resolve } from 'path';
|
||||
import { registerTsProject } from '@nx/js/src/internal';
|
||||
|
||||
import { getRootTsConfigPath } from '@nx/js';
|
||||
@ -45,7 +45,7 @@ export const createNodes: CreateNodes<EslintPluginOptions> = [
|
||||
configFilePath,
|
||||
projectRoot,
|
||||
options,
|
||||
context,
|
||||
context
|
||||
),
|
||||
},
|
||||
},
|
||||
@ -57,12 +57,9 @@ function buildEslintTargets(
|
||||
configFilePath: string,
|
||||
projectRoot: string,
|
||||
options: EslintPluginOptions,
|
||||
context: CreateNodesContext,
|
||||
context: CreateNodesContext
|
||||
) {
|
||||
const eslintConfig = getEslintConfig(
|
||||
configFilePath,
|
||||
context
|
||||
);
|
||||
const eslintConfig = getEslintConfig(configFilePath, context);
|
||||
|
||||
const targetDefaults = readTargetDefaultsForTarget(
|
||||
options.targetName,
|
||||
@ -72,10 +69,7 @@ function buildEslintTargets(
|
||||
|
||||
const namedInputs = getNamedInputs(projectRoot, context);
|
||||
|
||||
const targets: Record<
|
||||
string,
|
||||
TargetConfiguration<ExecutorOptions>
|
||||
> = {};
|
||||
const targets: Record<string, TargetConfiguration<ExecutorOptions>> = {};
|
||||
|
||||
const baseTargetConfig: TargetConfiguration<ExecutorOptions> = {
|
||||
executor: 'executorName',
|
||||
@ -92,9 +86,7 @@ function buildEslintTargets(
|
||||
targetDefaults?.inputs ?? 'production' in namedInputs
|
||||
? ['default', '^production']
|
||||
: ['default', '^default'],
|
||||
outputs:
|
||||
targetDefaults?.outputs ??
|
||||
getOutputs(projectRoot),
|
||||
outputs: targetDefaults?.outputs ?? getOutputs(projectRoot),
|
||||
options: {
|
||||
...baseTargetConfig.options,
|
||||
},
|
||||
@ -129,9 +121,7 @@ function getEslintConfig(
|
||||
return module.default ?? module;
|
||||
}
|
||||
|
||||
function getOutputs(
|
||||
projectRoot: string,
|
||||
): string[] {
|
||||
function getOutputs(projectRoot: string): string[] {
|
||||
function getOutput(path: string): string {
|
||||
if (path.startsWith('..')) {
|
||||
return join('{workspaceRoot}', join(projectRoot, path));
|
||||
@ -179,7 +169,7 @@ describe('@nx/eslint/plugin', () => {
|
||||
});
|
||||
|
||||
it('should create nodes', () => {
|
||||
mockEslintConfig({})
|
||||
mockEslintConfig({});
|
||||
const nodes = createNodesFunction(
|
||||
'TODO',
|
||||
{
|
||||
@ -192,10 +182,7 @@ describe('@nx/eslint/plugin', () => {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function mockEslintConfig(
|
||||
config: any
|
||||
) {
|
||||
function mockEslintConfig(config: any) {
|
||||
jest.mock(
|
||||
'TODO',
|
||||
() => ({
|
||||
@ -208,3 +195,30 @@ function mockEslintConfig(
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`create-nodes-plugin/generator generator should run successfully 3`] = `
|
||||
"import { formatFiles, getProjects, Tree } from '@nx/devkit';
|
||||
import { createNodes } from '../../plugins/plugin';
|
||||
|
||||
import { createProjectRootMappingsFromProjectConfigurations } from 'nx/src/project-graph/utils/find-project-for-path';
|
||||
import { replaceProjectConfigurationsWithPlugin } from '@nx/devkit/src/utils/replace-project-configuration-with-plugin';
|
||||
|
||||
export default async function update(tree: Tree) {
|
||||
const proj = Object.fromEntries(getProjects(tree).entries());
|
||||
|
||||
const rootMappings = createProjectRootMappingsFromProjectConfigurations(proj);
|
||||
|
||||
replaceProjectConfigurationsWithPlugin(
|
||||
tree,
|
||||
rootMappings,
|
||||
'@nx/eslint/plugin',
|
||||
createNodes,
|
||||
{
|
||||
targetName: 'TODO',
|
||||
}
|
||||
);
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
import { formatFiles, getProjects, Tree } from '@nx/devkit';
|
||||
import { createNodes } from '../../plugins/plugin';
|
||||
|
||||
import { createProjectRootMappingsFromProjectConfigurations } from 'nx/src/project-graph/utils/find-project-for-path';
|
||||
import { replaceProjectConfigurationsWithPlugin } from '@nx/devkit/src/utils/replace-project-configuration-with-plugin';
|
||||
|
||||
export default async function update(tree: Tree) {
|
||||
const proj = Object.fromEntries(getProjects(tree).entries());
|
||||
|
||||
const rootMappings = createProjectRootMappingsFromProjectConfigurations(proj);
|
||||
|
||||
replaceProjectConfigurationsWithPlugin(
|
||||
tree,
|
||||
rootMappings,
|
||||
'@nx/<%= dirName %>/plugin',
|
||||
createNodes,
|
||||
{
|
||||
targetName: 'TODO',
|
||||
}
|
||||
);
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -1,25 +1,43 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { Tree } from '@nx/devkit';
|
||||
import { addProjectConfiguration, Tree, writeJson } from '@nx/devkit';
|
||||
|
||||
import { generatorGenerator, GeneratorGeneratorSchema } from './generator';
|
||||
import { generatorGenerator } from './generator';
|
||||
import { setCwd } from '@nx/devkit/internal-testing-utils';
|
||||
|
||||
describe('create-nodes-plugin/generator generator', () => {
|
||||
let tree: Tree;
|
||||
const options: GeneratorGeneratorSchema = {};
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
|
||||
addProjectConfiguration(tree, 'eslint', {
|
||||
root: 'packages/eslint',
|
||||
targets: {
|
||||
build: {},
|
||||
},
|
||||
});
|
||||
|
||||
writeJson(tree, 'packages/eslint/package.json', {});
|
||||
|
||||
jest.spyOn(process, 'cwd').mockReturnValue('/virtual/packages/eslint');
|
||||
|
||||
setCwd('packages/eslint');
|
||||
});
|
||||
|
||||
it('should run successfully', async () => {
|
||||
await generatorGenerator(tree, options);
|
||||
await generatorGenerator(tree);
|
||||
expect(
|
||||
tree.read('packages/eslint/src/plugins/plugin.ts').toString()
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('packages/eslint/src/plugins/plugin.spec.ts').toString()
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree
|
||||
.read(
|
||||
'packages/eslint/src/migrations/update-17-2-0/add-eslint-plugin.ts'
|
||||
)
|
||||
.toString()
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,20 +1,27 @@
|
||||
import { generateFiles, names, Tree } from '@nx/devkit';
|
||||
import { formatFiles, generateFiles, names, Tree } from '@nx/devkit';
|
||||
import { basename, join, relative } from 'path';
|
||||
import migrationGenerator from '@nx/plugin/src/generators/migration/migration';
|
||||
|
||||
export interface GeneratorGeneratorSchema {}
|
||||
|
||||
export async function generatorGenerator(
|
||||
tree: Tree,
|
||||
options: GeneratorGeneratorSchema
|
||||
) {
|
||||
export async function generatorGenerator(tree: Tree) {
|
||||
const cwd = process.cwd();
|
||||
const { className, propertyName } = names(basename(cwd));
|
||||
|
||||
await migrationGenerator(tree, {
|
||||
name: `add-${basename(cwd)}-plugin`,
|
||||
packageVersion: '17.2.0-beta.0',
|
||||
description: `Add @nx/${basename(cwd)}/plugin`,
|
||||
nameAndDirectoryFormat: 'as-provided',
|
||||
directory: `src/migrations/update-17-2-0`,
|
||||
skipFormat: true,
|
||||
});
|
||||
|
||||
generateFiles(tree, join(__dirname, 'files'), relative(tree.root, cwd), {
|
||||
dirName: basename(cwd),
|
||||
className,
|
||||
propertyName,
|
||||
});
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
export default generatorGenerator;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user