fix(core): addPlugin should not conflict on project.json targets (#23264)

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

## Current Behavior
<!-- This is the behavior we have today -->
Calculating conflicts in target names does not consider if the
project.json defined targets will actually be impacted by the plugin
that wants to be added creating false negatives

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->
Calculating conflicts should be more accurate

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #22476
This commit is contained in:
Colum Ferry 2024-05-13 15:53:09 +01:00 committed by GitHub
parent 08654e1966
commit 85c8916087
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
59 changed files with 291 additions and 242 deletions

View File

@ -1,3 +1,5 @@
import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version';
import type { Tree } from '@nx/devkit'; import type { Tree } from '@nx/devkit';
import * as devkit from '@nx/devkit'; import * as devkit from '@nx/devkit';

View File

@ -1,3 +1,5 @@
import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version';
import { import {
DependencyType, DependencyType,

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import { readJson, updateJson } from '@nx/devkit'; import { readJson, updateJson } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import { import {
getProjects, getProjects,

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import type { Tree } from '@nx/devkit'; import type { Tree } from '@nx/devkit';
import { readJson } from '@nx/devkit'; import { readJson } from '@nx/devkit';

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import type { Tree } from '@nx/devkit'; import type { Tree } from '@nx/devkit';
import { readJson } from '@nx/devkit'; import { readJson } from '@nx/devkit';

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import type { Tree } from '@nx/devkit'; import type { Tree } from '@nx/devkit';
import * as devkit from '@nx/devkit'; import * as devkit from '@nx/devkit';

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import { E2eTestRunner } from '../../utils/test-runners'; import { E2eTestRunner } from '../../utils/test-runners';
import { import {

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import scamGenerator from '../scam/scam'; import scamGenerator from '../scam/scam';

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import { import {
readJson, readJson,

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import { import {
NxJsonConfiguration, NxJsonConfiguration,

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version';
import type { Tree } from '@nx/devkit'; import type { Tree } from '@nx/devkit';

View File

@ -1,3 +1,5 @@
import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version';
import type { Tree } from '@nx/devkit'; import type { Tree } from '@nx/devkit';
import { readJson, writeJson } from '@nx/devkit'; import { readJson, writeJson } from '@nx/devkit';

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import type { Tree } from '@nx/devkit'; import type { Tree } from '@nx/devkit';
import * as devkit from '@nx/devkit'; import * as devkit from '@nx/devkit';

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import { readJson, updateJson } from '@nx/devkit'; import { readJson, updateJson } from '@nx/devkit';
import * as devkit from '@nx/devkit'; import * as devkit from '@nx/devkit';

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import type { Tree } from '@nx/devkit'; import type { Tree } from '@nx/devkit';
import * as devkit from '@nx/devkit'; import * as devkit from '@nx/devkit';

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import { import {
readJson, readJson,

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import * as devkit from '@nx/devkit'; import * as devkit from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';

View File

@ -1,3 +1,5 @@
import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import { import {
ProjectGraph, ProjectGraph,
readJson, readJson,

View File

@ -80,7 +80,7 @@ export async function configurationGeneratorInternal(
}) })
); );
} else if (opts.addPlugin) { } else if (opts.addPlugin) {
await addPlugin(tree, projectGraph, false); await addPlugin(tree, false);
} }
const nxJson = readNxJson(tree); const nxJson = readNxJson(tree);

View File

@ -1,23 +1,17 @@
import { import {
addDependenciesToPackageJson, addDependenciesToPackageJson,
createProjectGraphAsync,
formatFiles, formatFiles,
GeneratorCallback, GeneratorCallback,
ProjectGraph,
readNxJson, readNxJson,
removeDependenciesFromPackageJson, removeDependenciesFromPackageJson,
runTasksInSerial, runTasksInSerial,
Tree, Tree,
updateNxJson, updateNxJson,
} from '@nx/devkit'; } from '@nx/devkit';
import { import { addPlugin as _addPlugin } from '@nx/devkit/src/utils/add-plugin';
addPlugin as _addPlugin,
generateCombinations,
} from '@nx/devkit/src/utils/add-plugin';
import { createNodes } from '../../plugins/plugin'; import { createNodes } from '../../plugins/plugin';
import { cypressVersion, nxVersion } from '../../utils/versions'; import { cypressVersion, nxVersion } from '../../utils/versions';
import { Schema } from './schema'; import { Schema } from './schema';
import { CypressPluginOptions } from '../../plugins/plugin';
function setupE2ETargetDefaults(tree: Tree) { function setupE2ETargetDefaults(tree: Tree) {
const nxJson = readNxJson(tree); const nxJson = readNxJson(tree);
@ -60,14 +54,9 @@ function updateDependencies(tree: Tree, options: Schema) {
return runTasksInSerial(...tasks); return runTasksInSerial(...tasks);
} }
export function addPlugin( export function addPlugin(tree: Tree, updatePackageScripts: boolean) {
tree: Tree,
graph: ProjectGraph,
updatePackageScripts: boolean
) {
return _addPlugin( return _addPlugin(
tree, tree,
graph,
'@nx/cypress/plugin', '@nx/cypress/plugin',
createNodes, createNodes,
{ {
@ -118,11 +107,7 @@ export async function cypressInitGeneratorInternal(
nxJson.useInferencePlugins !== false; nxJson.useInferencePlugins !== false;
if (options.addPlugin) { if (options.addPlugin) {
await addPlugin( await addPlugin(tree, options.updatePackageScripts);
tree,
await createProjectGraphAsync(),
options.updatePackageScripts
);
} else { } else {
setupE2ETargetDefaults(tree); setupE2ETargetDefaults(tree);
} }

View File

@ -1,6 +1,5 @@
import { import {
addDependenciesToPackageJson, addDependenciesToPackageJson,
createProjectGraphAsync,
formatFiles, formatFiles,
GeneratorCallback, GeneratorCallback,
readNxJson, readNxJson,
@ -8,11 +7,8 @@ import {
runTasksInSerial, runTasksInSerial,
Tree, Tree,
} from '@nx/devkit'; } from '@nx/devkit';
import { import { addPlugin } from '@nx/devkit/src/utils/add-plugin';
addPlugin, import { createNodes } from '../../plugins/plugin';
generateCombinations,
} from '@nx/devkit/src/utils/add-plugin';
import { createNodes, DetoxPluginOptions } from '../../plugins/plugin';
import { detoxVersion, nxVersion } from '../../utils/versions'; import { detoxVersion, nxVersion } from '../../utils/versions';
import { Schema } from './schema'; import { Schema } from './schema';
@ -38,7 +34,6 @@ export async function detoxInitGeneratorInternal(host: Tree, schema: Schema) {
if (schema.addPlugin) { if (schema.addPlugin) {
await addPlugin( await addPlugin(
host, host,
await createProjectGraphAsync(),
'@nx/detox/plugin', '@nx/detox/plugin',
createNodes, createNodes,
{ {

View File

@ -1,17 +1,50 @@
jest.mock('vite', () => ({
resolveConfig: jest.fn().mockImplementation(() => {
return Promise.resolve({
path: 'vite.config.ts',
config: {},
build: {},
dependencies: [],
});
}),
}));
jest.mock('@nx/vite/plugin', () => ({
createNodes: [
'**/vite.config.{js,cjs,mjs}',
(_, { targetName }) => ({
projects: {
app1: {
name: 'app1',
targets: {
[targetName]: { command: 'vite build' },
},
},
app2: {
name: 'app2',
targets: {
[targetName]: { command: 'vite build' },
},
},
},
}),
],
createDependencies: jest.fn(),
}));
import { createTreeWithEmptyWorkspace } from 'nx/src/generators/testing-utils/create-tree-with-empty-workspace'; import { createTreeWithEmptyWorkspace } from 'nx/src/generators/testing-utils/create-tree-with-empty-workspace';
import type { Tree } from 'nx/src/generators/tree'; import type { Tree } from 'nx/src/generators/tree';
import { readJson, writeJson } from 'nx/src/generators/utils/json'; import { readJson, writeJson } from 'nx/src/generators/utils/json';
import type { PackageJson } from 'nx/src/utils/package-json'; import type { PackageJson } from 'nx/src/utils/package-json';
import { CreateNodes } from 'nx/src/project-graph/plugins'; import { CreateNodes } from 'nx/src/project-graph/plugins';
import { ProjectGraph } from 'nx/src/devkit-exports';
import { TempFs } from 'nx/src/internal-testing-utils/temp-fs'; import { TempFs } from 'nx/src/internal-testing-utils/temp-fs';
import { addPlugin, generateCombinations } from './add-plugin'; import { addPlugin, generateCombinations } from './add-plugin';
import { updateNxJson } from 'nx/src/generators/utils/nx-json';
describe('addPlugin', () => { describe('addPlugin', () => {
let tree: Tree; let tree: Tree;
let createNodes: CreateNodes<{ targetName: string }>; let createNodes: CreateNodes<{ targetName: string }>;
let graph: ProjectGraph;
let fs: TempFs; let fs: TempFs;
beforeEach(async () => { beforeEach(async () => {
@ -20,39 +53,6 @@ describe('addPlugin', () => {
fs = new TempFs('add-plugin'); fs = new TempFs('add-plugin');
tree.root = fs.tempDir; tree.root = fs.tempDir;
graph = {
nodes: {
app1: {
name: 'app1',
type: 'app',
data: {
root: 'app1',
targets: {},
},
},
app2: {
name: 'app2',
type: 'app',
data: {
root: 'app2',
targets: {},
},
},
app3: {
name: 'app3',
type: 'app',
data: {
root: 'app3',
targets: {},
},
},
},
dependencies: {
app1: [],
app2: [],
app3: [],
},
};
createNodes = [ createNodes = [
'**/next.config.{js,cjs,mjs}', '**/next.config.{js,cjs,mjs}',
(_, { targetName }) => ({ (_, { targetName }) => ({
@ -84,41 +84,148 @@ describe('addPlugin', () => {
}); });
describe('adding the plugin', () => { describe('adding the plugin', () => {
it('should not conflicting with the existing graph', async () => { it('should not conflicting with the existing plugins', async () => {
graph.nodes.app1.data.targets.build = {}; // ARRANGE
graph.nodes.app2.data.targets.build1 = {}; const nxJson = readJson(tree, 'nx.json');
// app 3 doesn't have a next config, so it having this nxJson.plugins ??= [];
// target should not affect the plugin options nxJson.plugins.push({
graph.nodes.app3.data.targets.build2 = {}; plugin: '@nx/vite/plugin',
options: { targetName: 'build' },
});
updateNxJson(tree, nxJson);
fs.createFilesSync({
'app1/vite.config.js': '',
'app2/vite.config.js': '',
});
// ACT
await addPlugin( await addPlugin(
tree, tree,
graph,
'@nx/next/plugin', '@nx/next/plugin',
createNodes, createNodes,
{ {
targetName: ['build', 'build1', 'build2'], targetName: ['build', 'build1'],
}, },
true true
); );
expect(readJson(tree, 'nx.json').plugins).toContainEqual({ // ASSERT
expect(
readJson(tree, 'nx.json').plugins.find(
(p) => p.plugin === '@nx/next/plugin'
)
).toEqual({
plugin: '@nx/next/plugin', plugin: '@nx/next/plugin',
options: { options: {
targetName: 'build2', targetName: 'build1',
},
});
});
it('should not conflicting with existing project.json with executor and run-commands in plugin', async () => {
// ARRANGE
fs.createFilesSync({
'app1/project.json': JSON.stringify({
targets: { build: { executor: '@nx/next:build' } },
}),
});
// ACT
await addPlugin(
tree,
'@nx/next/plugin',
createNodes,
{
targetName: ['build'],
},
true
);
// ASSERT
expect(
readJson(tree, 'nx.json').plugins.find(
(p) => p.plugin === '@nx/next/plugin'
)
).toEqual({
plugin: '@nx/next/plugin',
options: {
targetName: 'build',
},
});
});
it('should not conflicting with existing project.json with executor and executor in plugin', async () => {
// ARRANGE
fs.createFilesSync({
'app1/project.json': JSON.stringify({
targets: { build: { executor: '@nx/next:build' } },
}),
});
createNodes = [
'**/next.config.{js,cjs,mjs}',
(_, { targetName }) => ({
projects: {
app1: {
name: 'app1',
targets: {
[targetName]: { executor: '@nx/next:build' },
},
},
app2: {
name: 'app2',
targets: {
[targetName]: { executor: '@nx/next:build' },
},
},
},
}),
];
// ACT
await addPlugin(
tree,
'@nx/next/plugin',
createNodes,
{
targetName: ['build'],
},
true
);
// ASSERT
expect(
readJson(tree, 'nx.json').plugins.find(
(p) => p.plugin === '@nx/next/plugin'
)
).toEqual({
plugin: '@nx/next/plugin',
options: {
targetName: 'build',
}, },
}); });
}); });
}); });
it('should throw an error if no non-conflicting options are provided', async () => { it('should throw an error if no non-conflicting options are provided', async () => {
graph.nodes.app1.data.targets.build = {}; const nxJson = readJson(tree, 'nx.json');
nxJson.plugins ??= [];
nxJson.plugins.push({
plugin: '@nx/vite/plugin',
options: { targetName: 'build' },
});
updateNxJson(tree, nxJson);
fs.createFilesSync({
'app1/vite.config.js': '',
'app2/vite.config.js': '',
});
try { try {
await addPlugin( await addPlugin(
tree, tree,
graph,
'@nx/next/plugin', '@nx/next/plugin',
createNodes, createNodes,
@ -147,7 +254,6 @@ describe('addPlugin', () => {
await addPlugin( await addPlugin(
tree, tree,
graph,
'@nx/next/plugin', '@nx/next/plugin',
createNodes, createNodes,
@ -185,7 +291,6 @@ describe('addPlugin', () => {
await addPlugin( await addPlugin(
tree, tree,
graph,
'@nx/next/plugin', '@nx/next/plugin',
createNodes, createNodes,
@ -234,7 +339,6 @@ describe('addPlugin', () => {
await addPlugin( await addPlugin(
tree, tree,
graph,
'@nx/cypress/plugin', '@nx/cypress/plugin',
createNodes, createNodes,
@ -259,7 +363,6 @@ describe('addPlugin', () => {
await addPlugin( await addPlugin(
tree, tree,
graph,
'@nx/next/plugin', '@nx/next/plugin',
createNodes, createNodes,
@ -300,7 +403,6 @@ describe('addPlugin', () => {
await addPlugin( await addPlugin(
tree, tree,
graph,
'@nx/next/plugin', '@nx/next/plugin',
createNodes, createNodes,
@ -342,7 +444,6 @@ describe('addPlugin', () => {
await addPlugin( await addPlugin(
tree, tree,
graph,
'@nx/next/plugin', '@nx/next/plugin',
createNodes, createNodes,
@ -384,7 +485,6 @@ describe('addPlugin', () => {
await addPlugin( await addPlugin(
tree, tree,
graph,
'@nx/next/plugin', '@nx/next/plugin',
createNodes, createNodes,

View File

@ -1,12 +1,12 @@
import type { PackageJson } from 'nx/src/utils/package-json'; import type { PackageJson } from 'nx/src/utils/package-json';
import type { ConfigurationResult } from 'nx/src/project-graph/utils/project-configuration-utils'; import type { ConfigurationResult } from 'nx/src/project-graph/utils/project-configuration-utils';
import * as yargs from 'yargs-parser'; import * as yargs from 'yargs-parser';
import { import {
CreateNodes, CreateNodes,
NxJsonConfiguration,
ProjectConfiguration, ProjectConfiguration,
ProjectGraph,
readJson, readJson,
readNxJson, readNxJson,
Tree, Tree,
@ -15,6 +15,10 @@ import {
} from 'nx/src/devkit-exports'; } from 'nx/src/devkit-exports';
import { import {
LoadedNxPlugin, LoadedNxPlugin,
loadNxPlugins,
isCompatibleTarget,
mergeTargetConfigurations,
deepEquals,
ProjectConfigurationsError, ProjectConfigurationsError,
retrieveProjectConfigurations, retrieveProjectConfigurations,
} from 'nx/src/devkit-internals'; } from 'nx/src/devkit-internals';
@ -25,7 +29,6 @@ import {
*/ */
export async function addPlugin<PluginOptions>( export async function addPlugin<PluginOptions>(
tree: Tree, tree: Tree,
graph: ProjectGraph,
pluginName: string, pluginName: string,
createNodesTuple: CreateNodes<PluginOptions>, createNodesTuple: CreateNodes<PluginOptions>,
options: Partial< options: Partial<
@ -33,11 +36,12 @@ export async function addPlugin<PluginOptions>(
>, >,
shouldUpdatePackageJsonScripts: boolean shouldUpdatePackageJsonScripts: boolean
): Promise<void> { ): Promise<void> {
const graphNodes = Object.values(graph.nodes);
const nxJson = readNxJson(tree); const nxJson = readNxJson(tree);
let pluginOptions: PluginOptions; let pluginOptions: PluginOptions;
let projConfigs: ConfigurationResult; let projConfigsWithExistingNxJsonPlugins: ConfigurationResult;
let projConfigsWithNewPlugin: ConfigurationResult;
let projConfigsWithCorePlugins: ConfigurationResult;
const combinations = generateCombinations(options); const combinations = generateCombinations(options);
optionsLoop: for (const _pluginOptions of combinations) { optionsLoop: for (const _pluginOptions of combinations) {
pluginOptions = _pluginOptions as PluginOptions; pluginOptions = _pluginOptions as PluginOptions;
@ -54,8 +58,21 @@ export async function addPlugin<PluginOptions>(
return; return;
} }
global.NX_GRAPH_CREATION = true; global.NX_GRAPH_CREATION = true;
try {
projConfigs = await retrieveProjectConfigurations( const [nxJsonPlugins] = await loadNxPlugins(
nxJson.plugins,
tree.root,
true
);
projConfigsWithExistingNxJsonPlugins =
await retrieveProjectConfigurationsWithPartialResult(
nxJsonPlugins,
tree.root,
nxJson
);
projConfigsWithNewPlugin =
await retrieveProjectConfigurationsWithPartialResult(
[ [
new LoadedNxPlugin( new LoadedNxPlugin(
{ {
@ -71,27 +88,38 @@ export async function addPlugin<PluginOptions>(
tree.root, tree.root,
nxJson nxJson
); );
} catch (e) {
// Errors are okay for this because we're only running 1 plugin
if (e instanceof ProjectConfigurationsError) {
projConfigs = e.partialProjectConfigurationsResult;
} else {
throw e;
}
}
global.NX_GRAPH_CREATION = false;
for (const projConfig of Object.values(projConfigs.projects)) { const [corePlugins] = await loadNxPlugins([], tree.root);
const node = graphNodes.find( projConfigsWithCorePlugins =
(node) => node.data.root === projConfig.root await retrieveProjectConfigurationsWithPartialResult(
corePlugins,
tree.root,
nxJson
); );
if (!node) { global.NX_GRAPH_CREATION = false;
continue;
}
for (const projConfig of Object.values(projConfigsWithNewPlugin.projects)) {
for (const targetName in projConfig.targets) { for (const targetName in projConfig.targets) {
if (node.data.targets[targetName]) { const existingPluginCreatedTarget =
projConfigsWithExistingNxJsonPlugins?.projects?.[projConfig.root]
?.targets?.[targetName];
const corePluginsCreatedTarget =
projConfigsWithCorePlugins?.projects?.[projConfig.root]?.targets?.[
targetName
];
if (
existingPluginCreatedTarget ||
(corePluginsCreatedTarget &&
!deepEquals(
corePluginsCreatedTarget,
mergeTargetConfigurations(
corePluginsCreatedTarget,
projConfig.targets[targetName]
)
))
) {
// Conflicting Target Name, check the next one // Conflicting Target Name, check the next one
pluginOptions = null; pluginOptions = null;
continue optionsLoop; continue optionsLoop;
@ -116,7 +144,7 @@ export async function addPlugin<PluginOptions>(
updateNxJson(tree, nxJson); updateNxJson(tree, nxJson);
if (shouldUpdatePackageJsonScripts) { if (shouldUpdatePackageJsonScripts) {
updatePackageScripts(tree, projConfigs); updatePackageScripts(tree, projConfigsWithNewPlugin);
} }
} }
@ -353,3 +381,26 @@ function _generateCombinations<T>(input: T[][]): T[][] {
); );
} }
} }
export async function retrieveProjectConfigurationsWithPartialResult(
plugins: LoadedNxPlugin[],
workspaceRoot: string,
nxJson: NxJsonConfiguration
): Promise<ConfigurationResult> {
let projConfigs: ConfigurationResult;
try {
projConfigs = await retrieveProjectConfigurations(
plugins,
workspaceRoot,
nxJson
);
} catch (e) {
// Errors are okay for this because we're only running 1 plugin
if (e instanceof ProjectConfigurationsError) {
projConfigs = e.partialProjectConfigurationsResult;
} else {
throw e;
}
}
return projConfigs;
}

View File

@ -1,3 +1,5 @@
import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import { Tree, readJson, writeJson } from '@nx/devkit'; import { Tree, readJson, writeJson } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from 'nx/src/devkit-testing-exports'; import { createTreeWithEmptyWorkspace } from 'nx/src/devkit-testing-exports';
import { WORKSPACE_PLUGIN_DIR } from '../../constants'; import { WORKSPACE_PLUGIN_DIR } from '../../constants';

View File

@ -1,5 +1,3 @@
import 'nx/src/internal-testing-utils/mock-project-graph';
import { NxJsonConfiguration, readJson, Tree, updateJson } from '@nx/devkit'; import { NxJsonConfiguration, readJson, Tree, updateJson } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { LinterInitOptions, lintInitGenerator } from './init'; import { LinterInitOptions, lintInitGenerator } from './init';

View File

@ -61,8 +61,6 @@ export async function initEsLint(
const hasPlugin = hasEslintPlugin(tree); const hasPlugin = hasEslintPlugin(tree);
const rootEslintFile = findEslintFile(tree); const rootEslintFile = findEslintFile(tree);
const graph = await createProjectGraphAsync();
const lintTargetNames = [ const lintTargetNames = [
'lint', 'lint',
'eslint:lint', 'eslint:lint',
@ -75,7 +73,6 @@ export async function initEsLint(
if (rootEslintFile && options.addPlugin && !hasPlugin) { if (rootEslintFile && options.addPlugin && !hasPlugin) {
await addPlugin( await addPlugin(
tree, tree,
graph,
'@nx/eslint/plugin', '@nx/eslint/plugin',
createNodes, createNodes,
{ {
@ -96,7 +93,6 @@ export async function initEsLint(
if (options.addPlugin) { if (options.addPlugin) {
await addPlugin( await addPlugin(
tree, tree,
graph,
'@nx/eslint/plugin', '@nx/eslint/plugin',
createNodes, createNodes,
{ {

View File

@ -1,5 +1,3 @@
import 'nx/src/internal-testing-utils/mock-project-graph';
import { readJson, Tree } from '@nx/devkit'; import { readJson, Tree } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { expoInitGenerator } from './init'; import { expoInitGenerator } from './init';

View File

@ -38,7 +38,6 @@ export async function expoInitGeneratorInternal(host: Tree, schema: Schema) {
if (schema.addPlugin) { if (schema.addPlugin) {
await addPlugin( await addPlugin(
host, host,
await createProjectGraphAsync(),
'@nx/expo/plugin', '@nx/expo/plugin',
createNodes, createNodes,
{ {

View File

@ -1,5 +1,3 @@
import 'nx/src/internal-testing-utils/mock-project-graph';
import { import {
type NxJsonConfiguration, type NxJsonConfiguration,
readJson, readJson,

View File

@ -103,7 +103,6 @@ export async function jestInitGeneratorInternal(
if (options.addPlugin) { if (options.addPlugin) {
await addPlugin( await addPlugin(
tree, tree,
await createProjectGraphAsync(),
'@nx/jest/plugin', '@nx/jest/plugin',
createNodes, createNodes,
{ {

View File

@ -1,23 +1,12 @@
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { readJson, Tree, ProjectGraph } from '@nx/devkit'; import { readJson, Tree } from '@nx/devkit';
import { nextInitGenerator } from './init'; import { nextInitGenerator } from './init';
let projectGraph: ProjectGraph;
jest.mock('@nx/devkit', () => ({
...jest.requireActual<any>('@nx/devkit'),
createProjectGraphAsync: jest.fn().mockImplementation(async () => {
return projectGraph;
}),
}));
describe('init', () => { describe('init', () => {
let tree: Tree; let tree: Tree;
beforeEach(() => { beforeEach(() => {
projectGraph = {
nodes: {},
dependencies: {},
};
tree = createTreeWithEmptyWorkspace(); tree = createTreeWithEmptyWorkspace();
}); });

View File

@ -55,7 +55,6 @@ export async function nextInitGeneratorInternal(
const { createNodes } = await import('../../plugins/plugin'); const { createNodes } = await import('../../plugins/plugin');
await addPlugin( await addPlugin(
host, host,
await createProjectGraphAsync(),
'@nx/next/plugin', '@nx/next/plugin',
createNodes, createNodes,
{ {

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import * as devkit from '@nx/devkit'; import * as devkit from '@nx/devkit';
import { import {

View File

@ -1,5 +1,3 @@
import 'nx/src/internal-testing-utils/mock-project-graph';
import { readJson, readNxJson, Tree } from '@nx/devkit'; import { readJson, readNxJson, Tree } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { nuxtInitGenerator } from './init'; import { nuxtInitGenerator } from './init';

View File

@ -8,7 +8,6 @@ import { updateDependencies } from './lib/utils';
export async function nuxtInitGenerator(host: Tree, schema: InitSchema) { export async function nuxtInitGenerator(host: Tree, schema: InitSchema) {
await addPlugin( await addPlugin(
host, host,
await createProjectGraphAsync(),
'@nx/nuxt/plugin', '@nx/nuxt/plugin',
createNodes, createNodes,
{ {

View File

@ -4,12 +4,16 @@
* These may not be available in certain version of Nx, so be sure to check them first. * These may not be available in certain version of Nx, so be sure to check them first.
*/ */
export { createTempNpmDirectory } from './utils/package-manager'; export { createTempNpmDirectory } from './utils/package-manager';
export { deepEquals } from './utils/json-diff';
export { getExecutorInformation } from './command-line/run/executor-utils'; export { getExecutorInformation } from './command-line/run/executor-utils';
export { readNxJson as readNxJsonFromDisk } from './config/nx-json'; export { readNxJson as readNxJsonFromDisk } from './config/nx-json';
export { calculateDefaultProjectName } from './config/calculate-default-project-name'; export { calculateDefaultProjectName } from './config/calculate-default-project-name';
export { retrieveProjectConfigurationsWithAngularProjects } from './project-graph/utils/retrieve-workspace-files'; export { retrieveProjectConfigurationsWithAngularProjects } from './project-graph/utils/retrieve-workspace-files';
export { mergeTargetConfigurations } from './project-graph/utils/project-configuration-utils'; export { mergeTargetConfigurations } from './project-graph/utils/project-configuration-utils';
export { readProjectConfigurationsFromRootMap } from './project-graph/utils/project-configuration-utils'; export {
readProjectConfigurationsFromRootMap,
isCompatibleTarget,
} from './project-graph/utils/project-configuration-utils';
export { splitTarget } from './utils/split-target'; export { splitTarget } from './utils/split-target';
export { combineOptionsForExecutor } from './utils/params'; export { combineOptionsForExecutor } from './utils/params';
export { sortObjectByKeys } from './utils/object-sort'; export { sortObjectByKeys } from './utils/object-sort';
@ -23,6 +27,9 @@ export {
findProjectForPath, findProjectForPath,
} from './project-graph/utils/find-project-for-path'; } from './project-graph/utils/find-project-for-path';
export { retrieveProjectConfigurations } from './project-graph/utils/retrieve-workspace-files'; export { retrieveProjectConfigurations } from './project-graph/utils/retrieve-workspace-files';
export { LoadedNxPlugin } from './project-graph/plugins/internal-api'; export {
LoadedNxPlugin,
loadNxPlugins,
} from './project-graph/plugins/internal-api';
export * from './project-graph/error-types'; export * from './project-graph/error-types';
export { registerTsProject } from './plugins/js/utils/register'; export { registerTsProject } from './plugins/js/utils/register';

View File

@ -0,0 +1,6 @@
jest.mock('nx/src/project-graph/plugins/loader', () => ({
...jest.requireActual('nx/src/project-graph/plugins/loader'),
loadNxPlugin: jest.fn().mockImplementation(() => {
return [Promise.resolve({}), () => {}];
}),
}));

View File

@ -98,7 +98,8 @@ export const nxPluginCache: Map<
export async function loadNxPlugins( export async function loadNxPlugins(
plugins: PluginConfiguration[], plugins: PluginConfiguration[],
root = workspaceRoot root = workspaceRoot,
skipDefaultPlugins = false
): Promise<[LoadedNxPlugin[], () => void]> { ): Promise<[LoadedNxPlugin[], () => void]> {
const result: Promise<LoadedNxPlugin>[] = []; const result: Promise<LoadedNxPlugin>[] = [];
@ -107,7 +108,9 @@ export async function loadNxPlugins(
? loadNxPluginInIsolation ? loadNxPluginInIsolation
: loadNxPlugin; : loadNxPlugin;
plugins = await normalizePlugins(plugins, root); plugins = skipDefaultPlugins
? plugins ?? []
: await normalizePlugins(plugins, root);
const cleanupFunctions: Array<() => void> = []; const cleanupFunctions: Array<() => void> = [];
for (const plugin of plugins) { for (const plugin of plugins) {

View File

@ -16,6 +16,7 @@ import {
} from '../../utils/workspace-context'; } from '../../utils/workspace-context';
import { buildAllWorkspaceFiles } from './build-all-workspace-files'; import { buildAllWorkspaceFiles } from './build-all-workspace-files';
import { join } from 'path'; import { join } from 'path';
import { ProjectConfigurationsError } from '../error-types';
/** /**
* Walks the workspace directory to create the `projectFileMap`, `ProjectConfigurations` and `allWorkspaceFiles` * Walks the workspace directory to create the `projectFileMap`, `ProjectConfigurations` and `allWorkspaceFiles`

View File

@ -1,24 +1,12 @@
import { ProjectGraph, readNxJson, Tree, updateNxJson } from '@nx/devkit'; import { readNxJson, Tree, updateNxJson } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { initGenerator } from './init'; import { initGenerator } from './init';
let projectGraph: ProjectGraph;
jest.mock('@nx/devkit', () => ({
...jest.requireActual<any>('@nx/devkit'),
createProjectGraphAsync: jest.fn().mockImplementation(async () => {
return projectGraph;
}),
}));
describe('@nx/playwright:init', () => { describe('@nx/playwright:init', () => {
let tree: Tree; let tree: Tree;
beforeEach(() => { beforeEach(() => {
projectGraph = {
nodes: {},
dependencies: {},
};
tree = createTreeWithEmptyWorkspace(); tree = createTreeWithEmptyWorkspace();
}); });
@ -43,7 +31,7 @@ describe('@nx/playwright:init', () => {
it('should not overwrite existing plugins', async () => { it('should not overwrite existing plugins', async () => {
updateNxJson(tree, { updateNxJson(tree, {
plugins: ['foo'], plugins: ['@nx/vite/plugin'],
}); });
await initGenerator(tree, { await initGenerator(tree, {
skipFormat: true, skipFormat: true,
@ -53,7 +41,7 @@ describe('@nx/playwright:init', () => {
const nxJson = readNxJson(tree); const nxJson = readNxJson(tree);
expect(nxJson.plugins).toMatchInlineSnapshot(` expect(nxJson.plugins).toMatchInlineSnapshot(`
[ [
"foo", "@nx/vite/plugin",
{ {
"options": { "options": {
"targetName": "e2e", "targetName": "e2e",

View File

@ -47,7 +47,6 @@ export async function initGeneratorInternal(
if (options.addPlugin) { if (options.addPlugin) {
await addPlugin( await addPlugin(
tree, tree,
await createProjectGraphAsync(),
'@nx/playwright/plugin', '@nx/playwright/plugin',
createNodes, createNodes,
{ targetName: ['e2e', 'playwright:e2e', 'playwright-e2e'] }, { targetName: ['e2e', 'playwright:e2e', 'playwright-e2e'] },

View File

@ -1,5 +1,3 @@
import 'nx/src/internal-testing-utils/mock-project-graph';
import { import {
Tree, Tree,
getProjects, getProjects,

View File

@ -1,5 +1,3 @@
import 'nx/src/internal-testing-utils/mock-project-graph';
import { Tree, readJson } from '@nx/devkit'; import { Tree, readJson } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { reactNativeInitGenerator } from './init'; import { reactNativeInitGenerator } from './init';

View File

@ -1,6 +1,5 @@
import { import {
addDependenciesToPackageJson, addDependenciesToPackageJson,
createProjectGraphAsync,
formatFiles, formatFiles,
GeneratorCallback, GeneratorCallback,
readNxJson, readNxJson,
@ -41,7 +40,6 @@ export async function reactNativeInitGeneratorInternal(
if (schema.addPlugin) { if (schema.addPlugin) {
await addPlugin( await addPlugin(
host, host,
await createProjectGraphAsync(),
'@nx/react-native/plugin', '@nx/react-native/plugin',
createNodes, createNodes,
{ {

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version';
import { import {

View File

@ -472,9 +472,6 @@ describe('React:CypressComponentTestConfiguration', () => {
buildTarget: 'my-app:build', buildTarget: 'my-app:build',
}); });
}).resolves; }).resolves;
expect(require('@nx/devkit').createProjectGraphAsync).toHaveBeenCalledTimes(
1
);
}); });
it('should setup cypress config files correctly', async () => { it('should setup cypress config files correctly', async () => {

View File

@ -1,5 +1,3 @@
import 'nx/src/internal-testing-utils/mock-project-graph';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { readJson } from '@nx/devkit'; import { readJson } from '@nx/devkit';
import initGenerator from './init'; import initGenerator from './init';

View File

@ -5,12 +5,8 @@ import {
readNxJson, readNxJson,
addDependenciesToPackageJson, addDependenciesToPackageJson,
runTasksInSerial, runTasksInSerial,
createProjectGraphAsync,
} from '@nx/devkit'; } from '@nx/devkit';
import { import { addPlugin } from '@nx/devkit/src/utils/add-plugin';
addPlugin,
generateCombinations,
} from '@nx/devkit/src/utils/add-plugin';
import { createNodes } from '../../plugins/plugin'; import { createNodes } from '../../plugins/plugin';
import { nxVersion, remixVersion } from '../../utils/versions'; import { nxVersion, remixVersion } from '../../utils/versions';
import { type Schema } from './schema'; import { type Schema } from './schema';
@ -46,7 +42,6 @@ export async function remixInitGeneratorInternal(tree: Tree, options: Schema) {
if (options.addPlugin) { if (options.addPlugin) {
await addPlugin( await addPlugin(
tree, tree,
await createProjectGraphAsync(),
'@nx/remix/plugin', '@nx/remix/plugin',
createNodes, createNodes,
{ {

View File

@ -1,27 +1,13 @@
import 'nx/src/internal-testing-utils/mock-project-graph';
import { Tree, readJson, ProjectGraph } from '@nx/devkit'; import { Tree, readJson, ProjectGraph } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { nxVersion } from '../../utils/versions'; import { nxVersion } from '../../utils/versions';
import { rollupInitGenerator } from './init'; import { rollupInitGenerator } from './init';
let projectGraph: ProjectGraph;
jest.mock('@nx/devkit', () => ({
...jest.requireActual<any>('@nx/devkit'),
createProjectGraphAsync: jest.fn().mockImplementation(async () => {
return projectGraph;
}),
}));
describe('rollupInitGenerator', () => { describe('rollupInitGenerator', () => {
let tree: Tree; let tree: Tree;
beforeEach(async () => { beforeEach(async () => {
projectGraph = {
nodes: {},
dependencies: {},
};
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
}); });

View File

@ -1,6 +1,5 @@
import { import {
addDependenciesToPackageJson, addDependenciesToPackageJson,
createProjectGraphAsync,
formatFiles, formatFiles,
GeneratorCallback, GeneratorCallback,
Tree, Tree,
@ -27,7 +26,6 @@ export async function rollupInitGenerator(tree: Tree, schema: Schema) {
if (schema.addPlugin) { if (schema.addPlugin) {
await addPlugin( await addPlugin(
tree, tree,
await createProjectGraphAsync(),
'@nx/rollup/plugin', '@nx/rollup/plugin',
createNodes, createNodes,
{ {

View File

@ -1,27 +1,11 @@
import { import { readJson, type NxJsonConfiguration, type Tree } from '@nx/devkit';
readJson,
type NxJsonConfiguration,
type Tree,
ProjectGraph,
} from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { initGenerator } from './init'; import { initGenerator } from './init';
let projectGraph: ProjectGraph;
jest.mock('@nx/devkit', () => ({
...jest.requireActual<any>('@nx/devkit'),
createProjectGraphAsync: jest.fn().mockImplementation(async () => {
return projectGraph;
}),
}));
describe('@nx/storybook:init', () => { describe('@nx/storybook:init', () => {
let tree: Tree; let tree: Tree;
beforeEach(() => { beforeEach(() => {
projectGraph = {
nodes: {},
dependencies: {},
};
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
}); });

View File

@ -1,6 +1,5 @@
import { import {
addDependenciesToPackageJson, addDependenciesToPackageJson,
createProjectGraphAsync,
formatFiles, formatFiles,
GeneratorCallback, GeneratorCallback,
installPackagesTask, installPackagesTask,
@ -104,7 +103,6 @@ export async function initGeneratorInternal(tree: Tree, schema: Schema) {
if (schema.addPlugin) { if (schema.addPlugin) {
await addPlugin( await addPlugin(
tree, tree,
await createProjectGraphAsync(),
'@nx/storybook/plugin', '@nx/storybook/plugin',
createNodes, createNodes,
{ {

View File

@ -1,7 +1,6 @@
import { import {
addDependenciesToPackageJson, addDependenciesToPackageJson,
NxJsonConfiguration, NxJsonConfiguration,
ProjectGraph,
readJson, readJson,
readNxJson, readNxJson,
Tree, Tree,
@ -10,14 +9,6 @@ import {
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { nxVersion } from '../../utils/versions'; import { nxVersion } from '../../utils/versions';
let projectGraph: ProjectGraph;
jest.mock('@nx/devkit', () => ({
...jest.requireActual<any>('@nx/devkit'),
createProjectGraphAsync: jest.fn().mockImplementation(async () => {
return projectGraph;
}),
}));
import { initGenerator } from './init'; import { initGenerator } from './init';
describe('@nx/vite:init', () => { describe('@nx/vite:init', () => {
@ -25,10 +16,6 @@ describe('@nx/vite:init', () => {
beforeEach(() => { beforeEach(() => {
tree = createTreeWithEmptyWorkspace(); tree = createTreeWithEmptyWorkspace();
projectGraph = {
nodes: {},
dependencies: {},
};
}); });
describe('dependencies for package.json', () => { describe('dependencies for package.json', () => {

View File

@ -1,5 +1,4 @@
import { import {
createProjectGraphAsync,
formatFiles, formatFiles,
GeneratorCallback, GeneratorCallback,
readNxJson, readNxJson,
@ -62,7 +61,6 @@ export async function initGeneratorInternal(
if (schema.addPlugin) { if (schema.addPlugin) {
await addPlugin( await addPlugin(
tree, tree,
await createProjectGraphAsync(),
'@nx/vite/plugin', '@nx/vite/plugin',
createNodes, createNodes,
{ {

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version';
import { readProjectConfiguration, Tree } from '@nx/devkit'; import { readProjectConfiguration, Tree } from '@nx/devkit';

View File

@ -1,5 +1,3 @@
import 'nx/src/internal-testing-utils/mock-project-graph';
import { readJson, Tree } from '@nx/devkit'; import { readJson, Tree } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';

View File

@ -1,6 +1,5 @@
import { import {
addDependenciesToPackageJson, addDependenciesToPackageJson,
createProjectGraphAsync,
formatFiles, formatFiles,
GeneratorCallback, GeneratorCallback,
readNxJson, readNxJson,
@ -25,7 +24,6 @@ export async function webpackInitGeneratorInternal(tree: Tree, schema: Schema) {
if (schema.addPlugin) { if (schema.addPlugin) {
await addPlugin( await addPlugin(
tree, tree,
await createProjectGraphAsync(),
'@nx/webpack/plugin', '@nx/webpack/plugin',
createNodes, createNodes,
{ {

View File

@ -1,4 +1,4 @@
import 'nx/src/internal-testing-utils/mock-project-graph'; import 'nx/src/internal-testing-utils/mock-load-nx-plugin';
import { readProjectConfiguration, Tree } from '@nx/devkit'; import { readProjectConfiguration, Tree } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';