From e412c32d3793e86a0628fd5e986926a95cf305b5 Mon Sep 17 00:00:00 2001 From: AgentEnder Date: Fri, 10 Nov 2023 16:47:09 -0500 Subject: [PATCH] feat(core): move plugins apis to support async create nodes --- docs/generated/devkit/CreateNodesAsync.md | 9 + docs/generated/devkit/CreateNodesFunction.md | 11 +- docs/generated/devkit/CreateNodesResult.md | 24 +++ docs/generated/devkit/NxPluginV2.md | 21 +- docs/generated/devkit/README.md | 2 + .../packages/devkit/documents/nx_devkit.md | 2 + .../module-federation-dev-server.impl.ts | 7 +- .../module-federation-dev-ssr.impl.ts | 4 +- .../cypress/src/utils/start-dev-server.ts | 3 +- .../src/executors/read-target-options.ts | 7 +- packages/nx/src/adapter/ngcli-adapter.ts | 49 +++-- .../nx/src/command-line/generate/generate.ts | 28 ++- .../command-line/generate/generator-utils.ts | 12 +- packages/nx/src/command-line/list/list.ts | 16 +- .../nx/src/command-line/migrate/migrate.ts | 7 + packages/nx/src/command-line/new/new.ts | 7 +- .../nx/src/command-line/release/version.ts | 11 +- .../nx/src/command-line/run/executor-utils.ts | 11 +- packages/nx/src/command-line/run/run.ts | 13 +- packages/nx/src/config/workspaces.ts | 9 +- packages/nx/src/devkit-exports.ts | 2 + packages/nx/src/native/index.d.ts | 4 +- .../src/native/tests/workspace_files.spec.ts | 120 +----------- .../nx/src/native/workspace/config_files.rs | 2 +- packages/nx/src/native/workspace/context.rs | 5 +- .../src/native/workspace/workspace_files.rs | 3 +- .../package-json-next-to-project-json.spec.ts | 8 +- .../project-json/build-nodes/project-json.ts | 4 +- .../src/project-graph/build-project-graph.ts | 10 +- packages/nx/src/project-graph/file-utils.ts | 18 +- .../utils/project-configuration-utils.ts | 113 ++++++++--- .../utils/retrieve-workspace-files.ts | 76 +++----- .../nx/src/tasks-runner/batch/run-batch.ts | 18 +- packages/nx/src/tasks-runner/utils.ts | 8 +- packages/nx/src/utils/nx-plugin.deprecated.ts | 36 +++- packages/nx/src/utils/nx-plugin.ts | 183 ++++++++---------- .../nx/src/utils/plugins/installed-plugins.ts | 12 +- .../nx/src/utils/plugins/local-plugins.ts | 1 + .../src/utils/plugins/plugin-capabilities.ts | 29 ++- packages/nx/src/utils/workspace-context.ts | 4 +- 40 files changed, 512 insertions(+), 397 deletions(-) create mode 100644 docs/generated/devkit/CreateNodesAsync.md create mode 100644 docs/generated/devkit/CreateNodesResult.md diff --git a/docs/generated/devkit/CreateNodesAsync.md b/docs/generated/devkit/CreateNodesAsync.md new file mode 100644 index 00000000000000..196a714ff7e105 --- /dev/null +++ b/docs/generated/devkit/CreateNodesAsync.md @@ -0,0 +1,9 @@ +# Type alias: CreateNodesAsync + +Ƭ **CreateNodesAsync**<`T`\>: readonly [projectFilePattern: string, createNodesFunction: CreateNodesFunctionAsync] + +#### Type parameters + +| Name | Type | +| :--- | :-------- | +| `T` | `unknown` | diff --git a/docs/generated/devkit/CreateNodesFunction.md b/docs/generated/devkit/CreateNodesFunction.md index 2d47929e9fd510..3f3df71e37667e 100644 --- a/docs/generated/devkit/CreateNodesFunction.md +++ b/docs/generated/devkit/CreateNodesFunction.md @@ -1,6 +1,6 @@ # Type alias: CreateNodesFunction -Ƭ **CreateNodesFunction**<`T`\>: (`projectConfigurationFile`: `string`, `options`: `T` \| `undefined`, `context`: [`CreateNodesContext`](../../devkit/documents/CreateNodesContext)) => { `externalNodes?`: `Record`<`string`, [`ProjectGraphExternalNode`](../../devkit/documents/ProjectGraphExternalNode)\> ; `projects?`: `Record`<`string`, `Optional`<[`ProjectConfiguration`](../../devkit/documents/ProjectConfiguration), `"root"`\>\> } +Ƭ **CreateNodesFunction**<`T`\>: (`projectConfigurationFile`: `string`, `options`: `T` \| `undefined`, `context`: [`CreateNodesContext`](../../devkit/documents/CreateNodesContext)) => [`CreateNodesResult`](../../devkit/documents/CreateNodesResult) #### Type parameters @@ -10,7 +10,7 @@ #### Type declaration -▸ (`projectConfigurationFile`, `options`, `context`): `Object` +▸ (`projectConfigurationFile`, `options`, `context`): [`CreateNodesResult`](../../devkit/documents/CreateNodesResult) A function which parses a configuration file into a set of nodes. Used for creating nodes for the [ProjectGraph](../../devkit/documents/ProjectGraph) @@ -25,9 +25,4 @@ Used for creating nodes for the [ProjectGraph](../../devkit/documents/ProjectGra ##### Returns -`Object` - -| Name | Type | Description | -| :--------------- | :---------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------- | -| `externalNodes?` | `Record`<`string`, [`ProjectGraphExternalNode`](../../devkit/documents/ProjectGraphExternalNode)\> | A map of external node name -> external node. External nodes do not have a root, so the key is their name. | -| `projects?` | `Record`<`string`, `Optional`<[`ProjectConfiguration`](../../devkit/documents/ProjectConfiguration), `"root"`\>\> | A map of project root -> project configuration | +[`CreateNodesResult`](../../devkit/documents/CreateNodesResult) diff --git a/docs/generated/devkit/CreateNodesResult.md b/docs/generated/devkit/CreateNodesResult.md new file mode 100644 index 00000000000000..6a7fafe07f03f4 --- /dev/null +++ b/docs/generated/devkit/CreateNodesResult.md @@ -0,0 +1,24 @@ +# Interface: CreateNodesResult + +## Table of contents + +### Properties + +- [externalNodes](../../devkit/documents/CreateNodesResult#externalnodes): Record<string, ProjectGraphExternalNode> +- [projects](../../devkit/documents/CreateNodesResult#projects): Record<string, Optional<ProjectConfiguration, "root">> + +## Properties + +### externalNodes + +• `Optional` **externalNodes**: `Record`<`string`, [`ProjectGraphExternalNode`](../../devkit/documents/ProjectGraphExternalNode)\> + +A map of external node name -> external node. External nodes do not have a root, so the key is their name. + +--- + +### projects + +• `Optional` **projects**: `Record`<`string`, `Optional`<[`ProjectConfiguration`](../../devkit/documents/ProjectConfiguration), `"root"`\>\> + +A map of project root -> project configuration diff --git a/docs/generated/devkit/NxPluginV2.md b/docs/generated/devkit/NxPluginV2.md index a8d3f804034724..092f88533ea3b6 100644 --- a/docs/generated/devkit/NxPluginV2.md +++ b/docs/generated/devkit/NxPluginV2.md @@ -1,19 +1,20 @@ -# Type alias: NxPluginV2 +# Type alias: NxPluginV2 -Ƭ **NxPluginV2**<`T`\>: `Object` +Ƭ **NxPluginV2**<`TOptions`, `TCreateNodes`\>: `Object` A plugin for Nx which creates nodes and dependencies for the [ProjectGraph](../../devkit/documents/ProjectGraph) #### Type parameters -| Name | Type | -| :--- | :-------- | -| `T` | `unknown` | +| Name | Type | +| :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `TOptions` | `unknown` | +| `TCreateNodes` | extends [`CreateNodes`](../../devkit/documents/CreateNodes)<`TOptions`\> \| [`CreateNodesAsync`](../../devkit/documents/CreateNodesAsync)<`TOptions`\> = [`CreateNodes`](../../devkit/documents/CreateNodes)<`TOptions`\> \| [`CreateNodesAsync`](../../devkit/documents/CreateNodesAsync)<`TOptions`\> | #### Type declaration -| Name | Type | Description | -| :-------------------- | :---------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | -| `createDependencies?` | [`CreateDependencies`](../../devkit/documents/CreateDependencies)<`T`\> | Provides a function to analyze files to create dependencies for the [ProjectGraph](../../devkit/documents/ProjectGraph) | -| `createNodes?` | [`CreateNodes`](../../devkit/documents/CreateNodes)<`T`\> | Provides a file pattern and function that retrieves configuration info from those files. e.g. { '\*_/_.csproj': buildProjectsFromCsProjFile } | -| `name` | `string` | - | +| Name | Type | Description | +| :-------------------- | :----------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | +| `createDependencies?` | [`CreateDependencies`](../../devkit/documents/CreateDependencies)<`TOptions`\> | Provides a function to analyze files to create dependencies for the [ProjectGraph](../../devkit/documents/ProjectGraph) | +| `createNodes?` | `TCreateNodes` | Provides a file pattern and function that retrieves configuration info from those files. e.g. { '\*_/_.csproj': buildProjectsFromCsProjFile } | +| `name` | `string` | - | diff --git a/docs/generated/devkit/README.md b/docs/generated/devkit/README.md index 6661d52e35a412..c0ea2140cd8ca5 100644 --- a/docs/generated/devkit/README.md +++ b/docs/generated/devkit/README.md @@ -25,6 +25,7 @@ It only uses language primitives and immutable objects - [CreateDependenciesContext](../../devkit/documents/CreateDependenciesContext) - [CreateNodesContext](../../devkit/documents/CreateNodesContext) +- [CreateNodesResult](../../devkit/documents/CreateNodesResult) - [DefaultTasksRunnerOptions](../../devkit/documents/DefaultTasksRunnerOptions) - [ExecutorContext](../../devkit/documents/ExecutorContext) - [ExecutorsJson](../../devkit/documents/ExecutorsJson) @@ -64,6 +65,7 @@ It only uses language primitives and immutable objects - [CreateDependencies](../../devkit/documents/CreateDependencies) - [CreateNodes](../../devkit/documents/CreateNodes) +- [CreateNodesAsync](../../devkit/documents/CreateNodesAsync) - [CreateNodesFunction](../../devkit/documents/CreateNodesFunction) - [CustomHasher](../../devkit/documents/CustomHasher) - [DynamicDependency](../../devkit/documents/DynamicDependency) diff --git a/docs/generated/packages/devkit/documents/nx_devkit.md b/docs/generated/packages/devkit/documents/nx_devkit.md index 6661d52e35a412..c0ea2140cd8ca5 100644 --- a/docs/generated/packages/devkit/documents/nx_devkit.md +++ b/docs/generated/packages/devkit/documents/nx_devkit.md @@ -25,6 +25,7 @@ It only uses language primitives and immutable objects - [CreateDependenciesContext](../../devkit/documents/CreateDependenciesContext) - [CreateNodesContext](../../devkit/documents/CreateNodesContext) +- [CreateNodesResult](../../devkit/documents/CreateNodesResult) - [DefaultTasksRunnerOptions](../../devkit/documents/DefaultTasksRunnerOptions) - [ExecutorContext](../../devkit/documents/ExecutorContext) - [ExecutorsJson](../../devkit/documents/ExecutorsJson) @@ -64,6 +65,7 @@ It only uses language primitives and immutable objects - [CreateDependencies](../../devkit/documents/CreateDependencies) - [CreateNodes](../../devkit/documents/CreateNodes) +- [CreateNodesAsync](../../devkit/documents/CreateNodesAsync) - [CreateNodesFunction](../../devkit/documents/CreateNodesFunction) - [CustomHasher](../../devkit/documents/CustomHasher) - [DynamicDependency](../../devkit/documents/DynamicDependency) diff --git a/packages/angular/src/builders/module-federation-dev-server/module-federation-dev-server.impl.ts b/packages/angular/src/builders/module-federation-dev-server/module-federation-dev-server.impl.ts index 9ae9936eb99632..ac74e8d6bb4ae3 100644 --- a/packages/angular/src/builders/module-federation-dev-server/module-federation-dev-server.impl.ts +++ b/packages/angular/src/builders/module-federation-dev-server/module-federation-dev-server.impl.ts @@ -203,7 +203,8 @@ function startDevRemotes( const { schema } = getExecutorInformation( collection, executor, - workspaceRoot + workspaceRoot, + workspaceProjects ); if ( (options.verbose && schema.additionalProperties) || @@ -223,6 +224,7 @@ function startDevRemotes( target: 'serve', configuration: context.target.configuration, runOptions, + projects: workspaceProjects, }, options.verbose ).then((obs) => { @@ -279,8 +281,7 @@ export function executeModuleFederationDevServerBuilder( configurationName: context.target.configuration, cwd: context.currentDirectory, isVerbose: options.verbose, - projectsConfigurations: - readProjectsConfigurationFromProjectGraph(projectGraph), + projectsConfigurations: { projects: workspaceProjects, version: 2 }, nxJsonConfiguration: readNxJson(), } ) diff --git a/packages/angular/src/builders/module-federation-dev-ssr/module-federation-dev-ssr.impl.ts b/packages/angular/src/builders/module-federation-dev-ssr/module-federation-dev-ssr.impl.ts index 5671a244c4caaf..a8699529bae8b1 100644 --- a/packages/angular/src/builders/module-federation-dev-ssr/module-federation-dev-ssr.impl.ts +++ b/packages/angular/src/builders/module-federation-dev-ssr/module-federation-dev-ssr.impl.ts @@ -98,7 +98,8 @@ export function executeModuleFederationDevSSRBuilder( const { schema } = getExecutorInformation( collection, executor, - workspaceRoot + workspaceRoot, + workspaceProjects ); if (schema.additionalProperties || 'verbose' in schema.properties) { @@ -141,6 +142,7 @@ export function executeModuleFederationDevSSRBuilder( target, configuration: context.target.configuration, runOptions, + projects: workspaceProjects, }, options.verbose ).then((obs) => diff --git a/packages/cypress/src/utils/start-dev-server.ts b/packages/cypress/src/utils/start-dev-server.ts index ea6f2d5c44b71a..cc90716cb4e381 100644 --- a/packages/cypress/src/utils/start-dev-server.ts +++ b/packages/cypress/src/utils/start-dev-server.ts @@ -194,7 +194,8 @@ ${e.message || e}`); const { schema } = getExecutorInformation( collection, executor, - context.root + context.root, + context.projectsConfigurations.projects ); // NOTE: schema won't have a default since readTargetOptions would have diff --git a/packages/devkit/src/executors/read-target-options.ts b/packages/devkit/src/executors/read-target-options.ts index 22220814a24149..ad193880b8d823 100644 --- a/packages/devkit/src/executors/read-target-options.ts +++ b/packages/devkit/src/executors/read-target-options.ts @@ -38,7 +38,12 @@ export function readTargetOptions( const ws = new Workspaces(context.root); const [nodeModule, executorName] = targetConfiguration.executor.split(':'); const { schema } = getExecutorInformation - ? getExecutorInformation(nodeModule, executorName, context.root) + ? getExecutorInformation( + nodeModule, + executorName, + context.root, + context.projectsConfigurations?.projects ?? context.workspace.projects + ) : // TODO(v18): remove readExecutor. This is to be backwards compatible with Nx 16.5 and below. (ws as any).readExecutor(nodeModule, executorName); diff --git a/packages/nx/src/adapter/ngcli-adapter.ts b/packages/nx/src/adapter/ngcli-adapter.ts index e4c52a77991be8..f74d664d547efc 100644 --- a/packages/nx/src/adapter/ngcli-adapter.ts +++ b/packages/nx/src/adapter/ngcli-adapter.ts @@ -35,7 +35,10 @@ import { getProjects, updateProjectConfiguration, } from '../generators/utils/project-configuration'; -import { createProjectGraphAsync } from '../project-graph/project-graph'; +import { + createProjectGraphAsync, + readProjectsConfigurationFromProjectGraph, +} from '../project-graph/project-graph'; import { readJsonFile } from '../utils/fileutils'; import { getNxRequirePaths } from '../utils/installation-directory'; import { parseJson } from '../utils/json'; @@ -79,7 +82,8 @@ export async function createBuilderContext( ); const architectHost = await getWrappedWorkspaceNodeModulesArchitectHost( workspace, - context.root + context.root, + context.projectsConfigurations.projects ); const registry = new schema.CoreSchemaRegistry(); @@ -156,6 +160,7 @@ export async function scheduleTarget( target: string; configuration: string; runOptions: any; + projects: Record; }, verbose: boolean ): Promise> { @@ -177,7 +182,8 @@ export async function scheduleTarget( const architectHost = await getWrappedWorkspaceNodeModulesArchitectHost( workspace, - root + root, + opts.projects ); const architect: Architect = new Architect(architectHost, registry); const run = await architect.scheduleTarget( @@ -204,7 +210,8 @@ export async function scheduleTarget( } function createNodeModulesEngineHost( - resolvePaths: string[] + resolvePaths: string[], + projects: Record ): import('@angular-devkit/schematics/tools').NodeModulesEngineHost { const NodeModulesEngineHost = require('@angular-devkit/schematics/tools') .NodeModulesEngineHost as typeof import('@angular-devkit/schematics/tools').NodeModulesEngineHost; @@ -226,7 +233,7 @@ function createNodeModulesEngineHost( const { json: { generators, schematics }, path: packageJsonPath, - } = readPluginPackageJson(name, paths); + } = readPluginPackageJson(name, projects, paths); if (!schematics && !generators) { throw new Error( @@ -255,7 +262,8 @@ function createNodeModulesEngineHost( function createWorkflow( fsHost: virtualFs.Host, root: string, - opts: any + opts: any, + projects: Record ): import('@angular-devkit/schematics/tools').NodeWorkflow { const NodeWorkflow: typeof import('@angular-devkit/schematics/tools').NodeWorkflow = require('@angular-devkit/schematics/tools').NodeWorkflow; @@ -269,7 +277,7 @@ function createWorkflow( ), resolvePaths: [process.cwd(), root], engineHostCreator: (options) => - createNodeModulesEngineHost(options.resolvePaths), + createNodeModulesEngineHost(options.resolvePaths, projects), }); workflow.registry.addPostTransform(schema.transforms.addUndefinedDefaults); workflow.engineHost.registerOptionsTransform( @@ -676,6 +684,7 @@ function findMatchingFileChange(host: Tree, path: Path) { export async function generate( root: string, opts: GenerateOptions, + projects: Record, verbose: boolean ) { const logger = getLogger(verbose); @@ -687,7 +696,7 @@ export async function generate( `ng-cli generator: ${opts.collectionName}:${opts.generatorName}` ) ); - const workflow = createWorkflow(fsHost, root, opts); + const workflow = createWorkflow(fsHost, root, opts, projects); const collection = getCollection(workflow, opts.collectionName); const schematic = collection.createSchematic(opts.generatorName, true); return ( @@ -769,6 +778,7 @@ export async function runMigration( root: string, packageName: string, migrationName: string, + projects: Record, isVerbose: boolean ) { const logger = getLogger(isVerbose); @@ -780,7 +790,7 @@ export async function runMigration( `ng-cli migration: ${packageName}:${migrationName}` ) ); - const workflow = createWorkflow(fsHost, root, {}); + const workflow = createWorkflow(fsHost, root, {}, projects); const collection = resolveMigrationsCollection(packageName); const record = { loggingQueue: [] as string[], error: false }; @@ -889,6 +899,9 @@ export function wrapAngularDevkitSchematic( require('./compat'); return async (host: Tree, generatorOptions: { [k: string]: any }) => { + const graph = await createProjectGraphAsync(); + const { projects } = readProjectsConfigurationFromProjectGraph(graph); + if ( mockedSchematics && mockedSchematics[`${collectionName}:${generatorName}`] @@ -950,7 +963,7 @@ export function wrapAngularDevkitSchematic( defaults: false, quiet: false, }; - const workflow = createWorkflow(fsHost, host.root, options); + const workflow = createWorkflow(fsHost, host.root, options, projects); // used for testing if (collectionResolutionOverrides) { @@ -1053,14 +1066,19 @@ function saveProjectsConfigurationsInWrappedSchematic( async function getWrappedWorkspaceNodeModulesArchitectHost( workspace: workspaces.WorkspaceDefinition, - root: string + root: string, + projects: Record ) { const { WorkspaceNodeModulesArchitectHost: AngularWorkspaceNodeModulesArchitectHost, } = await import('@angular-devkit/architect/node'); class WrappedWorkspaceNodeModulesArchitectHost extends AngularWorkspaceNodeModulesArchitectHost { - constructor(private workspace, private root) { + constructor( + private workspace, + private root: string, + private projects: Record + ) { super(workspace, root); } @@ -1090,6 +1108,7 @@ async function getWrappedWorkspaceNodeModulesArchitectHost( const { json: packageJson, path: packageJsonPath } = readPluginPackageJson( nodeModule, + this.projects, this.root ? [this.root, __dirname] : [__dirname] ); const executorsFile = packageJson.executors ?? packageJson.builders; @@ -1170,5 +1189,9 @@ async function getWrappedWorkspaceNodeModulesArchitectHost( } } - return new WrappedWorkspaceNodeModulesArchitectHost(workspace, root); + return new WrappedWorkspaceNodeModulesArchitectHost( + workspace, + root, + projects + ); } diff --git a/packages/nx/src/command-line/generate/generate.ts b/packages/nx/src/command-line/generate/generate.ts index 257f0ee4fb5781..b0de98758108dc 100644 --- a/packages/nx/src/command-line/generate/generate.ts +++ b/packages/nx/src/command-line/generate/generate.ts @@ -71,7 +71,12 @@ async function promptForCollection( resolvedCollectionName, normalizedGeneratorName, generatorConfiguration: { ['x-deprecated']: deprecated, hidden }, - } = getGeneratorInformation(collectionName, generatorName, workspaceRoot); + } = getGeneratorInformation( + collectionName, + generatorName, + workspaceRoot, + projectsConfiguration.projects + ); if (hidden) { continue; } @@ -96,7 +101,12 @@ async function promptForCollection( resolvedCollectionName, normalizedGeneratorName, generatorConfiguration: { ['x-deprecated']: deprecated, hidden }, - } = getGeneratorInformation(name, generatorName, workspaceRoot); + } = getGeneratorInformation( + name, + generatorName, + workspaceRoot, + projectsConfiguration.projects + ); if (hidden) { continue; } @@ -158,7 +168,12 @@ async function promptForCollection( return true; } try { - getGeneratorInformation(value, generatorName, workspaceRoot); + getGeneratorInformation( + value, + generatorName, + workspaceRoot, + projectsConfiguration.projects + ); return true; } catch { logger.error(`\nCould not find ${value}:${generatorName}`); @@ -316,7 +331,8 @@ export async function generate(cwd: string, args: { [k: string]: any }) { } = getGeneratorInformation( opts.collectionName, opts.generatorName, - workspaceRoot + workspaceRoot, + projectsConfigurations.projects ); if (deprecated) { @@ -360,7 +376,8 @@ export async function generate(cwd: string, args: { [k: string]: any }) { getGeneratorInformation( opts.collectionName, normalizedGeneratorName, - workspaceRoot + workspaceRoot, + projectsConfigurations.projects ).isNxGenerator ) { const host = new FsTree( @@ -400,6 +417,7 @@ export async function generate(cwd: string, args: { [k: string]: any }) { ...opts, generatorOptions: combinedOpts, }, + projectsConfigurations.projects, verbose ); } diff --git a/packages/nx/src/command-line/generate/generator-utils.ts b/packages/nx/src/command-line/generate/generator-utils.ts index 74c42072a40099..1fffe47dcbb005 100644 --- a/packages/nx/src/command-line/generate/generator-utils.ts +++ b/packages/nx/src/command-line/generate/generator-utils.ts @@ -4,6 +4,7 @@ import { GeneratorsJson, GeneratorsJsonEntry, } from '../../config/misc-interfaces'; +import { ProjectConfiguration } from '../../config/workspace-json-project-json'; import { getImplementationFactory, resolveSchema, @@ -14,7 +15,8 @@ import { readPluginPackageJson } from '../../utils/nx-plugin'; export function getGeneratorInformation( collectionName: string, generatorName: string, - root: string | null + root: string | null, + projects: Record ): { resolvedCollectionName: string; normalizedGeneratorName: string; @@ -30,7 +32,7 @@ export function getGeneratorInformation( generatorsJson, resolvedCollectionName, normalizedGeneratorName, - } = readGeneratorsJson(collectionName, generatorName, root); + } = readGeneratorsJson(collectionName, generatorName, root, projects); const generatorsDir = dirname(generatorsFilePath); const generatorConfig = generatorsJson.generators?.[normalizedGeneratorName] || @@ -71,7 +73,8 @@ export function getGeneratorInformation( export function readGeneratorsJson( collectionName: string, generator: string, - root: string | null + root: string | null, + projects: Record ): { generatorsFilePath: string; generatorsJson: GeneratorsJson; @@ -86,6 +89,7 @@ export function readGeneratorsJson( } else { const { json: packageJson, path: packageJsonPath } = readPluginPackageJson( collectionName, + projects, root ? [root, __dirname] : [__dirname] ); const generatorsFile = packageJson.generators ?? packageJson.schematics; @@ -109,7 +113,7 @@ export function readGeneratorsJson( if (!normalizedGeneratorName) { for (let parent of generatorsJson.extends || []) { try { - return readGeneratorsJson(parent, generator, root); + return readGeneratorsJson(parent, generator, root, projects); } catch (e) {} } diff --git a/packages/nx/src/command-line/list/list.ts b/packages/nx/src/command-line/list/list.ts index 09a459c83b9a3e..caa7281723e9ac 100644 --- a/packages/nx/src/command-line/list/list.ts +++ b/packages/nx/src/command-line/list/list.ts @@ -31,19 +31,19 @@ export interface ListArgs { * */ export async function listHandler(args: ListArgs): Promise { + const nxJson = readNxJson(); + const projectGraph = await createProjectGraphAsync({ exitOnError: true }); + const projects = readProjectsConfigurationFromProjectGraph(projectGraph); + if (args.plugin) { - await listPluginCapabilities(args.plugin); + await listPluginCapabilities(args.plugin, projects.projects); } else { - const nxJson = readNxJson(); const corePlugins = fetchCorePlugins(); - const projectGraph = await createProjectGraphAsync({ exitOnError: true }); - const localPlugins = await getLocalWorkspacePlugins( - readProjectsConfigurationFromProjectGraph(projectGraph), - nxJson - ); + const localPlugins = await getLocalWorkspacePlugins(projects, nxJson); const installedPlugins = await getInstalledPluginsAndCapabilities( - workspaceRoot + workspaceRoot, + projects.projects ); if (localPlugins.size) { diff --git a/packages/nx/src/command-line/migrate/migrate.ts b/packages/nx/src/command-line/migrate/migrate.ts index 0045b2518638c8..c766c5cd0cff24 100644 --- a/packages/nx/src/command-line/migrate/migrate.ts +++ b/packages/nx/src/command-line/migrate/migrate.ts @@ -62,6 +62,10 @@ import { readNxJson } from '../../config/configuration'; import { runNxSync } from '../../utils/child-process'; import { daemonClient } from '../../daemon/client/client'; import { isNxCloudUsed } from '../../utils/nx-cloud-utils'; +import { + createProjectGraphAsync, + readProjectsConfigurationFromProjectGraph, +} from '../../project-graph/project-graph'; export interface ResolvedMigrationConfiguration extends MigrationsJson { packageGroup?: ArrayPackageGroup; @@ -1404,6 +1408,9 @@ export async function executeMigrations( root, m.package, m.name, + readProjectsConfigurationFromProjectGraph( + await createProjectGraphAsync() + ).projects, isVerbose ); diff --git a/packages/nx/src/command-line/new/new.ts b/packages/nx/src/command-line/new/new.ts index ce12c1c7a49ba8..e4251ba1a4bb9d 100644 --- a/packages/nx/src/command-line/new/new.ts +++ b/packages/nx/src/command-line/new/new.ts @@ -16,7 +16,12 @@ export async function newWorkspace(cwd: string, args: { [k: string]: any }) { async () => { const isInteractive = args.interactive; const { normalizedGeneratorName, schema, implementationFactory } = - getGeneratorInformation('@nx/workspace/generators.json', 'new', null); + getGeneratorInformation( + '@nx/workspace/generators.json', + 'new', + null, + {} + ); removeSpecialFlags(args); const combinedOpts = await combineOptionsForGenerator( args, diff --git a/packages/nx/src/command-line/release/version.ts b/packages/nx/src/command-line/release/version.ts index e0d3b4ed9af40d..719ffe3b657b12 100644 --- a/packages/nx/src/command-line/release/version.ts +++ b/packages/nx/src/command-line/release/version.ts @@ -51,6 +51,7 @@ export interface ReleaseVersionGeneratorSchema { export async function versionHandler(args: VersionOptions): Promise { const projectGraph = await createProjectGraphAsync({ exitOnError: true }); + const { projects } = readProjectsConfigurationFromProjectGraph(projectGraph); const nxJson = readNxJson(); if (args.verbose) { @@ -98,6 +99,7 @@ export async function versionHandler(args: VersionOptions): Promise { releaseGroup.version.generator ), configGeneratorOptions: releaseGroup.version.generatorOptions, + projects, }); const releaseGroupProjectNames = Array.from( @@ -133,6 +135,7 @@ export async function versionHandler(args: VersionOptions): Promise { releaseGroup.version.generator ), configGeneratorOptions: releaseGroup.version.generatorOptions, + projects, }); await runVersionOnProjects( @@ -260,9 +263,15 @@ function resolveGeneratorData({ collectionName, generatorName, configGeneratorOptions, + projects, }): GeneratorData { const { normalizedGeneratorName, schema, implementationFactory } = - getGeneratorInformation(collectionName, generatorName, workspaceRoot); + getGeneratorInformation( + collectionName, + generatorName, + workspaceRoot, + projects + ); return { collectionName, diff --git a/packages/nx/src/command-line/run/executor-utils.ts b/packages/nx/src/command-line/run/executor-utils.ts index 9dd4439a904226..e12f7e752cd83e 100644 --- a/packages/nx/src/command-line/run/executor-utils.ts +++ b/packages/nx/src/command-line/run/executor-utils.ts @@ -14,6 +14,7 @@ import { resolveSchema, } from '../../config/schema-utils'; import { getNxRequirePaths } from '../../utils/installation-directory'; +import { ProjectConfiguration } from '../../config/workspace-json-project-json'; export function normalizeExecutorSchema( schema: Partial @@ -40,7 +41,8 @@ const cachedExecutorInformation = {}; export function getExecutorInformation( nodeModule: string, executor: string, - root: string + root: string, + projects: Record ): ExecutorConfig & { isNgCompat: boolean; isNxExecutor: boolean } { try { const key = cacheKey(nodeModule, executor, root); @@ -49,7 +51,8 @@ export function getExecutorInformation( const { executorsFilePath, executorConfig, isNgCompat } = readExecutorJson( nodeModule, executor, - root + root, + projects ); const executorsDir = dirname(executorsFilePath); const schemaPath = resolveSchema(executorConfig.schema, executorsDir); @@ -95,7 +98,8 @@ export function getExecutorInformation( function readExecutorJson( nodeModule: string, executor: string, - root: string + root: string, + projects: Record ): { executorsFilePath: string; executorConfig: { @@ -108,6 +112,7 @@ function readExecutorJson( } { const { json: packageJson, path: packageJsonPath } = readPluginPackageJson( nodeModule, + projects, root ? [root, __dirname, process.cwd(), ...getNxRequirePaths()] : [__dirname, process.cwd(), ...getNxRequirePaths()] diff --git a/packages/nx/src/command-line/run/run.ts b/packages/nx/src/command-line/run/run.ts index 001e025d05ad58..06f36c4377b372 100644 --- a/packages/nx/src/command-line/run/run.ts +++ b/packages/nx/src/command-line/run/run.ts @@ -96,7 +96,8 @@ async function parseExecutorAndTarget( const { schema, implementationFactory } = getExecutorInformation( nodeModule, executor, - root + root, + projectsConfigurations.projects ); return { executor, implementationFactory, nodeModule, schema, targetConfig }; @@ -154,7 +155,14 @@ async function runExecutorInternal( isVerbose ); - if (getExecutorInformation(nodeModule, executor, root).isNxExecutor) { + if ( + getExecutorInformation( + nodeModule, + executor, + root, + projectsConfigurations.projects + ).isNxExecutor + ) { const implementation = implementationFactory() as Executor; const r = implementation(combinedOptions, { root, @@ -190,6 +198,7 @@ async function runExecutorInternal( target, configuration, runOptions: combinedOptions, + projects: projectsConfigurations.projects, }, isVerbose ); diff --git a/packages/nx/src/config/workspaces.ts b/packages/nx/src/config/workspaces.ts index 98a9b9bde18eb4..6885bd136b0eed 100644 --- a/packages/nx/src/config/workspaces.ts +++ b/packages/nx/src/config/workspaces.ts @@ -1,9 +1,12 @@ import { dirname } from 'path'; +import { + readCachedProjectGraph, + readProjectsConfigurationFromProjectGraph, +} from '../project-graph/project-graph'; import type { NxJsonConfiguration } from './nx-json'; import { readNxJson } from './nx-json'; import { ProjectsConfigurations } from './workspace-json-project-json'; -import { retrieveProjectConfigurationsSync } from '../project-graph/utils/retrieve-workspace-files'; // TODO(v18): remove this class /** @@ -19,9 +22,7 @@ export class Workspaces { const nxJson = readNxJson(this.root); return { - version: 2, - projects: retrieveProjectConfigurationsSync(this.root, nxJson) - .projectNodes, + ...readProjectsConfigurationFromProjectGraph(readCachedProjectGraph()), ...nxJson, }; } diff --git a/packages/nx/src/devkit-exports.ts b/packages/nx/src/devkit-exports.ts index a75dd08b724647..f4865db6ce66fc 100644 --- a/packages/nx/src/devkit-exports.ts +++ b/packages/nx/src/devkit-exports.ts @@ -51,7 +51,9 @@ export type { NxPluginV2, ProjectTargetConfigurator, CreateNodes, + CreateNodesAsync, CreateNodesFunction, + CreateNodesResult, CreateNodesContext, CreateDependencies, CreateDependenciesContext, diff --git a/packages/nx/src/native/index.d.ts b/packages/nx/src/native/index.d.ts index edce7e11cff6bf..a13b8762cf5def 100644 --- a/packages/nx/src/native/index.d.ts +++ b/packages/nx/src/native/index.d.ts @@ -134,9 +134,9 @@ export class Watcher { export class WorkspaceContext { workspaceRoot: string constructor(workspaceRoot: string) - getWorkspaceFiles(globs: Array, parseConfigurations: (arg0: Array) => Promise>): object | null + getWorkspaceFiles(globs: Array, parseConfigurations: (arg0: Array) => Promise>): Promise glob(globs: Array): Array - getProjectConfigurations(globs: Array, parseConfigurations: (arg0: Array) => Promise>): object + getProjectConfigurations(globs: Array, parseConfigurations: (arg0: Array) => Promise>): Promise> incrementalUpdate(updatedFiles: Array, deletedFiles: Array): Record allFileData(): Array } diff --git a/packages/nx/src/native/tests/workspace_files.spec.ts b/packages/nx/src/native/tests/workspace_files.spec.ts index 5782cec7fc6e27..21d527546dbfcc 100644 --- a/packages/nx/src/native/tests/workspace_files.spec.ts +++ b/packages/nx/src/native/tests/workspace_files.spec.ts @@ -51,20 +51,9 @@ describe('workspace files', () => { let globs = ['project.json', '**/project.json', 'libs/*/package.json']; const context = new WorkspaceContext(fs.tempDir); -<<<<<<< HEAD - let { projectFileMap, globalFiles } = context.getWorkspaceFiles( + let { projectFileMap, globalFiles } = await context.getWorkspaceFiles( globs, createParseConfigurationsFunction(fs.tempDir) -======= - let { projectFileMap, projectConfigurations, globalFiles } = - (await context.getWorkspaceFiles( - globs, - createParseConfigurationsFunction(fs.tempDir) - )) as any; - - let sortedConfigs = Object.values(projectConfigurations).sort((a, b) => - a['name'].localeCompare(b['name']) ->>>>>>> 6d080c8bf (feat(core): make createNodes async) ); expect(projectFileMap).toMatchInlineSnapshot(` @@ -139,7 +128,6 @@ describe('workspace files', () => { `); }); -<<<<<<< HEAD it('should assign files to the root project if it exists', async () => { const fs = new TempFs('workspace-files'); const nxJson: NxJsonConfiguration = {}; @@ -160,7 +148,7 @@ describe('workspace files', () => { const context = new WorkspaceContext(fs.tempDir); const globs = ['project.json', '**/project.json', '**/package.json']; - const { globalFiles, projectFileMap } = context.getWorkspaceFiles( + const { globalFiles, projectFileMap } = await context.getWorkspaceFiles( globs, createParseConfigurationsFunction(fs.tempDir) ); @@ -191,110 +179,6 @@ describe('workspace files', () => { ] `); }); -======= - // it('should assign files to the root project if it exists', async () => { - // const fs = new TempFs('workspace-files'); - // const nxJson: NxJsonConfiguration = {}; - // await fs.createFiles({ - // './nx.json': JSON.stringify(nxJson), - // './package.json': JSON.stringify({ - // name: 'repo-name', - // version: '0.0.0', - // dependencies: {}, - // }), - // './project.json': JSON.stringify({ - // name: 'repo-name', - // }), - // './src/index.js': '', - // './jest.config.js': '', - // }); - // - // const context = new WorkspaceContext(fs.tempDir); - // - // const globs = ['project.json', '**/project.json', '**/package.json']; - // const { globalFiles, projectFileMap } = context.getWorkspaceFiles( - // globs, - // createParseConfigurationsFunction(fs.tempDir) - // ); - // - // expect(globalFiles).toEqual([]); - // expect(projectFileMap['repo-name']).toMatchInlineSnapshot(` - // [ - // { - // "file": "jest.config.js", - // "hash": "3244421341483603138", - // }, - // { - // "file": "nx.json", - // "hash": "1389868326933519382", - // }, - // { - // "file": "package.json", - // "hash": "14409636362330144230", - // }, - // { - // "file": "project.json", - // "hash": "4357927788053707201", - // }, - // { - // "file": "src/index.js", - // "hash": "3244421341483603138", - // }, - // ] - // `); - // }); - // - // it('should dedupe configuration files', async () => { - // const fs = new TempFs('workspace-files'); - // const nxJson: NxJsonConfiguration = {}; - // await fs.createFiles({ - // './nx.json': JSON.stringify(nxJson), - // './package.json': JSON.stringify({ - // name: 'repo-name', - // version: '0.0.0', - // dependencies: {}, - // }), - // './project.json': JSON.stringify({ - // name: 'repo-name', - // }), - // './libs/project1/project.json': JSON.stringify({ - // name: 'project1', - // }), - // './libs/project1/package.json': JSON.stringify({ - // name: 'project1', - // }), - // './libs/project1/index.js': '', - // }); - // - // const context = new WorkspaceContext(fs.tempDir); - // let globs = ['project.json', '**/project.json', '**/package.json']; - // - // let nodes = context.getProjectConfigurations(globs, (filenames) => { - // const res = {}; - // for (const filename of filenames) { - // const json = readJsonFile(join(fs.tempDir, filename)); - // res[json.name] = { - // ...json, - // root: dirname(filename), - // }; - // } - // return { - // externalNodes: {}, - // projectNodes: res, - // }; - // }); - // expect(nodes.projectNodes).toEqual({ - // project1: { - // name: 'project1', - // root: 'libs/project1', - // }, - // 'repo-name': expect.objectContaining({ - // name: 'repo-name', - // root: '.', - // }), - // }); - // }); ->>>>>>> 6d080c8bf (feat(core): make createNodes async) // describe('errors', () => { // it('it should infer names of configuration files without a name', async () => { diff --git a/packages/nx/src/native/workspace/config_files.rs b/packages/nx/src/native/workspace/config_files.rs index 4a0cb2e8591c4d..1b85116af77628 100644 --- a/packages/nx/src/native/workspace/config_files.rs +++ b/packages/nx/src/native/workspace/config_files.rs @@ -1,7 +1,7 @@ use crate::native::glob::build_glob_set; use crate::native::utils::path::Normalize; use std::collections::HashMap; -use napi::bindgen_prelude::{Object, Promise}; +use napi::bindgen_prelude::Promise; use crate::native::workspace::errors::{InternalWorkspaceErrors, WorkspaceErrors}; use rayon::prelude::*; diff --git a/packages/nx/src/native/workspace/context.rs b/packages/nx/src/native/workspace/context.rs index 937b5da0fccdca..fa65ffab92c0ef 100644 --- a/packages/nx/src/native/workspace/context.rs +++ b/packages/nx/src/native/workspace/context.rs @@ -16,7 +16,6 @@ use xxhash_rust::xxh3; use crate::native::walker::nx_walker; use crate::native::workspace::errors::WorkspaceErrors; -use crate::native::workspace::workspace_files::NxWorkspaceFiles; use crate::native::workspace::{config_files, workspace_files}; #[napi] @@ -149,7 +148,7 @@ impl WorkspaceContext { } } - #[napi] + #[napi(ts_return_type = "Promise")] pub fn get_workspace_files( &self, env: Env, @@ -173,7 +172,7 @@ impl WorkspaceContext { config_files::glob_files(globs, self.files_worker.get_files().as_deref()) } - #[napi] + #[napi(ts_return_type = "Promise>")] pub fn get_project_configurations( &self, env: Env, diff --git a/packages/nx/src/native/workspace/workspace_files.rs b/packages/nx/src/native/workspace/workspace_files.rs index 1caeaad02824a7..5df4d31cec9030 100644 --- a/packages/nx/src/native/workspace/workspace_files.rs +++ b/packages/nx/src/native/workspace/workspace_files.rs @@ -2,9 +2,8 @@ use napi::bindgen_prelude::{Object, Promise}; use std::collections::HashMap; use std::path::{Path, PathBuf}; -use napi::{Env, JsObject}; +use napi::Env; use rayon::prelude::*; -use serde_json::Value; use tracing::trace; use crate::native::types::FileData; diff --git a/packages/nx/src/plugins/project-json/build-nodes/package-json-next-to-project-json.spec.ts b/packages/nx/src/plugins/project-json/build-nodes/package-json-next-to-project-json.spec.ts index 01f84e3cee47f1..bdcc5088f74c8c 100644 --- a/packages/nx/src/plugins/project-json/build-nodes/package-json-next-to-project-json.spec.ts +++ b/packages/nx/src/plugins/project-json/build-nodes/package-json-next-to-project-json.spec.ts @@ -3,22 +3,18 @@ import * as memfs from 'memfs'; import '../../../internal-testing-utils/mock-fs'; import { CreatePackageJsonProjectsNextToProjectJson } from './package-json-next-to-project-json'; -import { - CreateNodesContext, - CreateNodesFunction, -} from '../../../utils/nx-plugin'; +import { CreateNodesContext } from '../../../utils/nx-plugin'; const { createNodes } = CreatePackageJsonProjectsNextToProjectJson; describe('nx project.json plugin', () => { let context: CreateNodesContext; - let createNodesFunction: CreateNodesFunction; + let createNodesFunction = createNodes[1]; beforeEach(() => { context = { nxJsonConfiguration: {}, workspaceRoot: '/root', }; - createNodesFunction = createNodes[1]; }); it('should build projects from project.json', () => { diff --git a/packages/nx/src/plugins/project-json/build-nodes/project-json.ts b/packages/nx/src/plugins/project-json/build-nodes/project-json.ts index bfaa8d03fe0569..3536326e3a1d6f 100644 --- a/packages/nx/src/plugins/project-json/build-nodes/project-json.ts +++ b/packages/nx/src/plugins/project-json/build-nodes/project-json.ts @@ -3,9 +3,9 @@ import { dirname, join } from 'node:path'; import { ProjectConfiguration } from '../../../config/workspace-json-project-json'; import { toProjectName } from '../../../config/workspaces'; import { readJsonFile } from '../../../utils/fileutils'; -import { NxPluginV2 } from '../../../utils/nx-plugin'; +import { CreateNodes, NxPluginV2 } from '../../../utils/nx-plugin'; -export const CreateProjectJsonProjectsPlugin: NxPluginV2 = { +export const CreateProjectJsonProjectsPlugin: NxPluginV2 = { name: 'nx-core-build-project-json-nodes', createNodes: [ '{project.json,**/project.json}', diff --git a/packages/nx/src/project-graph/build-project-graph.ts b/packages/nx/src/project-graph/build-project-graph.ts index 31b36cab6ba7c2..eddb5ffe767aa2 100644 --- a/packages/nx/src/project-graph/build-project-graph.ts +++ b/packages/nx/src/project-graph/build-project-graph.ts @@ -22,10 +22,8 @@ import { import { getRootTsConfigPath } from '../plugins/js/utils/typescript'; import { FileMap, - ProjectFileMap, ProjectGraph, ProjectGraphExternalNode, - ProjectGraphProcessorContext, } from '../config/project-graph'; import { readJsonFile } from '../utils/fileutils'; import { NxJsonConfiguration } from '../config/nx-json'; @@ -34,6 +32,7 @@ import { ProjectConfiguration } from '../config/workspace-json-project-json'; import { readNxJson } from '../config/configuration'; import { existsSync } from 'fs'; import { PackageJson } from '../utils/package-json'; +import { getNxRequirePaths } from '../utils/installation-directory'; let storedFileMap: FileMap | null = null; let storedAllWorkspaceFiles: FileData[] | null = null; @@ -231,7 +230,12 @@ async function updateProjectGraphWithPlugins( context: CreateDependenciesContext, initProjectGraph: ProjectGraph ) { - const plugins = await loadNxPlugins(context.nxJsonConfiguration?.plugins); + const plugins = await loadNxPlugins( + context.nxJsonConfiguration?.plugins, + getNxRequirePaths(), + context.workspaceRoot, + context.projects + ); let graph = initProjectGraph; for (const { plugin } of plugins) { try { diff --git a/packages/nx/src/project-graph/file-utils.ts b/packages/nx/src/project-graph/file-utils.ts index d95d866fb3d4e5..fd8e3447a92183 100644 --- a/packages/nx/src/project-graph/file-utils.ts +++ b/packages/nx/src/project-graph/file-utils.ts @@ -14,7 +14,10 @@ import { } from './project-graph'; import { toOldFormat } from '../adapter/angular-json'; import { getIgnoreObject } from '../utils/ignore'; -import { retrieveProjectConfigurationsSync } from './utils/retrieve-workspace-files'; +import { retrieveProjectConfigurationPathsWithoutPluginInference } from './utils/retrieve-workspace-files'; +import { buildProjectsConfigurationsFromProjectPathsAndPlugins } from './utils/project-configuration-utils'; +import { NxJsonConfiguration } from '../config/nx-json'; +import { getDefaultPluginsSync } from '../utils/nx-plugin.deprecated'; export interface Change { type: string; @@ -139,7 +142,7 @@ export function readWorkspaceConfig(opts: { } catch { configuration = { version: 2, - projects: retrieveProjectConfigurationsSync(root, nxJson).projectNodes, + projects: getProjectsSyncNoInference(root, nxJson).projects, }; } if (opts.format === 'angularCli') { @@ -164,3 +167,14 @@ export function readPackageJson(): any { export { FileData }; // TODO(17): Remove these exports export { readNxJson, workspaceLayout } from '../config/configuration'; + +function getProjectsSyncNoInference(root: string, nxJson: NxJsonConfiguration) { + const paths = retrieveProjectConfigurationPathsWithoutPluginInference(root); + return buildProjectsConfigurationsFromProjectPathsAndPlugins( + nxJson, + paths, + getDefaultPluginsSync(root), + root, + true + ); +} diff --git a/packages/nx/src/project-graph/utils/project-configuration-utils.ts b/packages/nx/src/project-graph/utils/project-configuration-utils.ts index 2f34174ebbb44e..cce0e7be663617 100644 --- a/packages/nx/src/project-graph/utils/project-configuration-utils.ts +++ b/packages/nx/src/project-graph/utils/project-configuration-utils.ts @@ -5,8 +5,9 @@ import { TargetConfiguration, } from '../../config/workspace-json-project-json'; import { NX_PREFIX } from '../../utils/logger'; -import { LoadedNxPlugin } from '../../utils/nx-plugin'; +import { CreateNodesResult, LoadedNxPlugin } from '../../utils/nx-plugin'; import { workspaceRoot } from '../../utils/workspace-root'; +import { output } from '../../utils/output'; import minimatch = require('minimatch'); @@ -88,18 +89,48 @@ export function mergeProjectConfigurationIntoRootMap( ); } +type ConfigurationResult = { + projects: Record; + externalNodes: Record; + rootMap: Record; +}; + +/** + * ** DO NOT USE ** - Please use without the `skipAsync` parameter. + * @deprecated + * @todo(@agentender): Remove in Nx 18 alongside the removal of its usage. + */ export function buildProjectsConfigurationsFromProjectPathsAndPlugins( nxJson: NxJsonConfiguration, projectFiles: string[], // making this parameter allows devkit to pick up newly created projects plugins: LoadedNxPlugin[], - root: string = workspaceRoot -): { - projects: Record; - externalNodes: Record; - rootMap: Record; -} { - const projectRootMap: Map = new Map(); - const externalNodes: Record = {}; + root: string, + skipAsync: true +): ConfigurationResult; + +/** + * Transforms a list of project paths into a map of project configurations. + * + * @param nxJson The NxJson configuration + * @param projectFiles A list of files identified as projects + * @param plugins The plugins that should be used to infer project configuration + * @param root The workspace root + */ +export function buildProjectsConfigurationsFromProjectPathsAndPlugins( + nxJson: NxJsonConfiguration, + projectFiles: string[], // making this parameter allows devkit to pick up newly created projects + plugins: LoadedNxPlugin[], + root: string, + skipAsync?: false +): Promise; +export function buildProjectsConfigurationsFromProjectPathsAndPlugins( + nxJson: NxJsonConfiguration, + projectFiles: string[], // making this parameter allows devkit to pick up newly created projects + plugins: LoadedNxPlugin[], + root: string = workspaceRoot, + skipAsync: boolean = false +): ConfigurationResult | Promise { + const results: Array> = []; // We iterate over plugins first - this ensures that plugins specified first take precedence. for (const { plugin, options } of plugins) { @@ -109,29 +140,51 @@ export function buildProjectsConfigurationsFromProjectPathsAndPlugins( } for (const file of projectFiles) { if (minimatch(file, pattern, { dot: true })) { - const { projects: projectNodes, externalNodes: pluginExternalNodes } = + results.push( createNodes(file, options, { nxJsonConfiguration: nxJson, workspaceRoot: root, - }); - for (const node in projectNodes) { - mergeProjectConfigurationIntoRootMap(projectRootMap, { - // If root is specified in config, that will overwrite this. - // Specifying it here though allows plugins to return something like - // { - // projects: { - // [root]: { targets: buildTargetsFromFile(f) } - // } - // } - // Otherwise, the root would have to be specified in the config as well - // which would be a bit redundant. - root: node, - ...projectNodes[node], - }); - } - Object.assign(externalNodes, pluginExternalNodes); + }) + ); + } + } + } + + return skipAsync + ? combineSyncConfigurationResults(results) + : combineAsyncConfigurationResults(results); +} + +function combineSyncConfigurationResults( + results: (CreateNodesResult | Promise)[] +): ConfigurationResult { + const projectRootMap: Map = new Map(); + const externalNodes: Record = {}; + + let warned = false; + for (const result of results) { + if (typeof result === 'object' && 'then' in result) { + if (!warned) { + output.warn({ + title: 'One or more plugins in this workspace are async.', + bodyLines: [ + 'Configuration from these plugins will not be visible to readWorkspaceConfig or readWorkspaceConfiguration. If you are using these methods, consider reading project info from the graph with createProjectGraphAsync instead.', + 'If you are not using one of these methods, please open an issue at http://github.com/nrwl/nx', + ], + }); + warned = true; } + continue; + } + const { projects: projectNodes, externalNodes: pluginExternalNodes } = + result; + for (const node in projectNodes) { + mergeProjectConfigurationIntoRootMap(projectRootMap, { + root: node, + ...projectNodes[node], + }); } + Object.assign(externalNodes, pluginExternalNodes); } const rootMap = createRootMap(projectRootMap); @@ -143,6 +196,12 @@ export function buildProjectsConfigurationsFromProjectPathsAndPlugins( }; } +function combineAsyncConfigurationResults( + results: Array> +): Promise { + return Promise.all(results).then((r) => combineSyncConfigurationResults(r)); +} + export function readProjectConfigurationsFromRootMap( projectRootMap: Map ) { diff --git a/packages/nx/src/project-graph/utils/retrieve-workspace-files.ts b/packages/nx/src/project-graph/utils/retrieve-workspace-files.ts index 8e36a2f5acea94..cd26dee52fa58e 100644 --- a/packages/nx/src/project-graph/utils/retrieve-workspace-files.ts +++ b/packages/nx/src/project-graph/utils/retrieve-workspace-files.ts @@ -21,13 +21,7 @@ import { getNxPackageJsonWorkspacesPlugin, } from '../../../plugins/package-json-workspaces'; import { buildProjectsConfigurationsFromProjectPathsAndPlugins } from './project-configuration-utils'; -import { - LoadedNxPlugin, - loadNxPlugins, - loadNxPluginsSync, - NxPluginV2, - unregisterPluginTSTranspiler, -} from '../../utils/nx-plugin'; +import { LoadedNxPlugin, loadNxPlugins } from '../../utils/nx-plugin'; import { CreateProjectJsonProjectsPlugin } from '../../plugins/project-json/build-nodes/project-json'; import { globWithWorkspaceContext, @@ -63,11 +57,11 @@ export async function retrieveWorkspaceFiles( let projects: Record; let externalNodes: Record; - const { projectFileMap, globalFiles } = getNxWorkspaceFilesFromContext( + const { projectFileMap, globalFiles } = (await getNxWorkspaceFilesFromContext( workspaceRoot, globs, - (configs: string[]) => { - const projectConfigurations = createProjectConfigurations( + async (configs: string[]) => { + const projectConfigurations = await createProjectConfigurations( workspaceRoot, nxJson, configs, @@ -79,7 +73,7 @@ export async function retrieveWorkspaceFiles( externalNodes = projectConfigurations.externalNodes; return projectConfigurations.rootMap; } - ) as NxWorkspaceFiles; + )) as NxWorkspaceFiles; performance.mark('get-workspace-files:end'); performance.measure( 'get-workspace-files', @@ -148,49 +142,30 @@ export async function retrieveProjectConfigurationsWithAngularProjects( return _retrieveProjectConfigurations(workspaceRoot, nxJson, plugins, globs); } -/** - * @deprecated Use {@link retrieveProjectConfigurations} instead. - */ -export function retrieveProjectConfigurationsSync( - workspaceRoot: string, - nxJson: NxJsonConfiguration -): { - externalNodes: Record; - projectNodes: Record; -} { - const plugins = loadNxPluginsSync( - nxJson?.plugins ?? [], - getNxRequirePaths(workspaceRoot), - workspaceRoot - ); - - const globs = configurationGlobs(workspaceRoot, plugins); - return _retrieveProjectConfigurations(workspaceRoot, nxJson, plugins, globs); -} - function _retrieveProjectConfigurations( workspaceRoot: string, nxJson: NxJsonConfiguration, plugins: LoadedNxPlugin[], globs: string[] -): { +): Promise<{ externalNodes: Record; projectNodes: Record; -} { +}> { let result: { externalNodes: Record; projectNodes: Record; }; - getProjectConfigurationsFromContext( + return getProjectConfigurationsFromContext( workspaceRoot, globs, - (configs: string[]) => { - const { projects, externalNodes, rootMap } = createProjectConfigurations( - workspaceRoot, - nxJson, - configs, - plugins - ); + async (configs: string[]) => { + const { projects, externalNodes, rootMap } = + await createProjectConfigurations( + workspaceRoot, + nxJson, + configs, + plugins + ); result = { projectNodes: projects, @@ -199,8 +174,7 @@ function _retrieveProjectConfigurations( return rootMap; } - ); - return result; + ).then(() => result); } export async function retrieveProjectConfigurationPaths( @@ -226,9 +200,9 @@ const projectsWithoutPluginCache = new Map< >(); // TODO: This function is called way too often, it should be optimized without this cache -export function retrieveProjectConfigurationsWithoutPluginInference( +export async function retrieveProjectConfigurationsWithoutPluginInference( root: string -): Record { +): Promise> { const nxJson = readNxJson(root); const projectGlobPatterns = configurationGlobsWithoutPlugins(root); const cacheKey = root + ',' + projectGlobPatterns.join(','); @@ -241,8 +215,8 @@ export function retrieveProjectConfigurationsWithoutPluginInference( getProjectConfigurationsFromContext( root, projectGlobPatterns, - (configs: string[]) => { - const projectConfigurations = createProjectConfigurations( + async (configs: string[]) => { + const projectConfigurations = await createProjectConfigurations( root, nxJson, configs, @@ -280,20 +254,20 @@ function buildAllWorkspaceFiles( return fileData; } -export function createProjectConfigurations( +export async function createProjectConfigurations( workspaceRoot: string, nxJson: NxJsonConfiguration, configFiles: string[], plugins: LoadedNxPlugin[] -): { +): Promise<{ projects: Record; externalNodes: Record; rootMap: Record; -} { +}> { performance.mark('build-project-configs:start'); const { projects, externalNodes, rootMap } = - buildProjectsConfigurationsFromProjectPathsAndPlugins( + await buildProjectsConfigurationsFromProjectPathsAndPlugins( nxJson, configFiles, plugins, diff --git a/packages/nx/src/tasks-runner/batch/run-batch.ts b/packages/nx/src/tasks-runner/batch/run-batch.ts index e286a952dbe05e..cb27c67daba776 100644 --- a/packages/nx/src/tasks-runner/batch/run-batch.ts +++ b/packages/nx/src/tasks-runner/batch/run-batch.ts @@ -16,10 +16,19 @@ import { import { readNxJson } from '../../config/configuration'; import { isAsyncIterator } from '../../utils/async-iterator'; import { getExecutorInformation } from '../../command-line/run/executor-utils'; +import { ProjectConfiguration } from '../../config/workspace-json-project-json'; -function getBatchExecutor(executorName: string) { +function getBatchExecutor( + executorName: string, + projects: Record +) { const [nodeModule, exportName] = executorName.split(':'); - return getExecutorInformation(nodeModule, exportName, workspaceRoot); + return getExecutorInformation( + nodeModule, + exportName, + workspaceRoot, + projects + ); } async function runTasks( @@ -32,7 +41,10 @@ async function runTasks( const projectsConfigurations = readProjectsConfigurationFromProjectGraph(projectGraph); const nxJsonConfiguration = readNxJson(); - const batchExecutor = getBatchExecutor(executorName); + const batchExecutor = getBatchExecutor( + executorName, + projectsConfigurations.projects + ); const tasks = Object.values(batchTaskGraph.tasks); const context: ExecutorContext = { root: workspaceRoot, diff --git a/packages/nx/src/tasks-runner/utils.ts b/packages/nx/src/tasks-runner/utils.ts index d4ba94544bbbd9..955dfee1cb5ad4 100644 --- a/packages/nx/src/tasks-runner/utils.ts +++ b/packages/nx/src/tasks-runner/utils.ts @@ -10,6 +10,7 @@ import { serializeOverridesIntoCommandLine } from '../utils/serialize-overrides- import { splitByColons } from '../utils/split-target'; import { getExecutorInformation } from '../command-line/run/executor-utils'; import { CustomHasher } from '../config/misc-interfaces'; +import { readProjectsConfigurationFromProjectGraph } from '../project-graph/project-graph'; export function getCommandAsString(execCommand: string, task: Task) { const args = getPrintableCommandArgsForTask(task); @@ -262,7 +263,12 @@ export async function getExecutorForTask( const executor = await getExecutorNameForTask(task, projectGraph); const [nodeModule, executorName] = executor.split(':'); - return getExecutorInformation(nodeModule, executorName, workspaceRoot); + return getExecutorInformation( + nodeModule, + executorName, + workspaceRoot, + readProjectsConfigurationFromProjectGraph(projectGraph).projects + ); } export async function getCustomHasher( diff --git a/packages/nx/src/utils/nx-plugin.deprecated.ts b/packages/nx/src/utils/nx-plugin.deprecated.ts index 2e94094010d5cb..0ad20d8ddb75b0 100644 --- a/packages/nx/src/utils/nx-plugin.deprecated.ts +++ b/packages/nx/src/utils/nx-plugin.deprecated.ts @@ -1,5 +1,25 @@ +import { getNxPackageJsonWorkspacesPlugin } from '../../plugins/package-json-workspaces'; +import { + NxAngularJsonPlugin, + shouldMergeAngularProjects, +} from '../adapter/angular-json'; +import { NxJsonConfiguration, PluginConfiguration } from '../config/nx-json'; import { ProjectGraphProcessor } from '../config/project-graph'; -import { TargetConfiguration } from '../config/workspace-json-project-json'; +import { + ProjectConfiguration, + TargetConfiguration, +} from '../config/workspace-json-project-json'; +import { CreateProjectJsonProjectsPlugin } from '../plugins/project-json/build-nodes/project-json'; +import { retrieveProjectConfigurationsWithoutPluginInference } from '../project-graph/utils/retrieve-workspace-files'; +import { getNxRequirePaths } from './installation-directory'; +import { + ensurePluginIsV2, + getPluginPathAndName, + LoadedNxPlugin, + nxPluginCache, + NxPluginV2, +} from './nx-plugin'; +import { workspaceRoot } from './workspace-root'; /** * @deprecated Add targets to the projects in a {@link CreateNodes} function instead. This will be removed in Nx 18 @@ -30,3 +50,17 @@ export type NxPluginV1 = { */ projectFilePatterns?: string[]; }; + +/** + * @todo(@agentender) v18: Remove this fn when we remove readWorkspaceConfig + */ +export function getDefaultPluginsSync(root: string): LoadedNxPlugin[] { + const plugins: NxPluginV2[] = [require('../plugins/js')]; + + if (shouldMergeAngularProjects(root, false)) { + plugins.push(require('../adapter/angular-json').NxAngularJsonPlugin); + } + return plugins.map((p) => ({ + plugin: p, + })); +} diff --git a/packages/nx/src/utils/nx-plugin.ts b/packages/nx/src/utils/nx-plugin.ts index b890afac47c869..c84f470c520470 100644 --- a/packages/nx/src/utils/nx-plugin.ts +++ b/packages/nx/src/utils/nx-plugin.ts @@ -27,20 +27,26 @@ import { normalizePath } from './path'; import { dirname, join } from 'path'; import { getNxRequirePaths } from './installation-directory'; import { readTsConfig } from '../plugins/js/utils/typescript'; -import { NxJsonConfiguration, PluginConfiguration } from '../config/nx-json'; +import { + NxJsonConfiguration, + PluginConfiguration, + readNxJson, +} from '../config/nx-json'; import type * as ts from 'typescript'; -import { retrieveProjectConfigurationsWithoutPluginInference } from '../project-graph/utils/retrieve-workspace-files'; import { NxPluginV1 } from './nx-plugin.deprecated'; import { RawProjectGraphDependency } from '../project-graph/project-graph-builder'; import { combineGlobPatterns } from './globs'; -import { - NxAngularJsonPlugin, - shouldMergeAngularProjects, -} from '../adapter/angular-json'; +import { shouldMergeAngularProjects } from '../adapter/angular-json'; import { getNxPackageJsonWorkspacesPlugin } from '../../plugins/package-json-workspaces'; import { CreateProjectJsonProjectsPlugin } from '../plugins/project-json/build-nodes/project-json'; import { CreatePackageJsonProjectsNextToProjectJson } from '../plugins/project-json/build-nodes/package-json-next-to-project-json'; +import { + mergeProjectConfigurationIntoRootMap, + readProjectConfigurationsFromRootMap, +} from '../project-graph/utils/project-configuration-utils'; +import { globWithWorkspaceContext } from './workspace-context'; +import { retrieveProjectConfigurationsWithoutPluginInference } from '../project-graph/utils/retrieve-workspace-files'; /** * Context for {@link CreateNodesFunction} @@ -58,7 +64,19 @@ export type CreateNodesFunction = ( projectConfigurationFile: string, options: T | undefined, context: CreateNodesContext -) => { +) => CreateNodesResult; + +/** + * A function which parses a configuration file into a set of nodes. + * Used for creating nodes for the {@link ProjectGraph} + */ +export type CreateNodesFunctionAsync = ( + projectConfigurationFile: string, + options: T | undefined, + context: CreateNodesContext +) => Promise; + +export interface CreateNodesResult { /** * A map of project root -> project configuration */ @@ -68,7 +86,7 @@ export type CreateNodesFunction = ( * A map of external node name -> external node. External nodes do not have a root, so the key is their name. */ externalNodes?: Record; -}; +} /** * A pair of file patterns and {@link CreateNodesFunction} @@ -78,6 +96,11 @@ export type CreateNodes = readonly [ createNodesFunction: CreateNodesFunction ]; +export type CreateNodesAsync = readonly [ + projectFilePattern: string, + createNodesFunction: CreateNodesFunctionAsync +]; + /** * Context for {@link CreateDependencies} */ @@ -122,20 +145,25 @@ export type CreateDependencies = ( /** * A plugin for Nx which creates nodes and dependencies for the {@link ProjectGraph} */ -export type NxPluginV2 = { +export type NxPluginV2< + TOptions = unknown, + TCreateNodes extends CreateNodes | CreateNodesAsync = + | CreateNodes + | CreateNodesAsync +> = { name: string; /** * Provides a file pattern and function that retrieves configuration info from * those files. e.g. { '**\/*.csproj': buildProjectsFromCsProjFile } */ - createNodes?: CreateNodes; + createNodes?: TCreateNodes; // Todo(@AgentEnder): This shouldn't be a full processor, since its only responsible for defining edges between projects. What do we want the API to be? /** * Provides a function to analyze files to create dependencies for the {@link ProjectGraph} */ - createDependencies?: CreateDependencies; + createDependencies?: CreateDependencies; }; export * from './nx-plugin.deprecated'; @@ -154,11 +182,12 @@ export type LoadedNxPlugin = { // holding resolved nx plugin objects. // Allows loadNxPlugins to be called multiple times w/o // executing resolution mulitple times. -let nxPluginCache: Map = new Map(); +export const nxPluginCache: Map = new Map(); -function getPluginPathAndName( +export function getPluginPathAndName( moduleName: string, paths: string[], + projects: Record, root: string ) { let pluginPath: string; @@ -168,7 +197,12 @@ function getPluginPathAndName( }); } catch (e) { if (e.code === 'MODULE_NOT_FOUND') { - const plugin = resolveLocalNxPlugin(moduleName, root); + const plugin = resolveLocalNxPlugin( + moduleName, + readNxJson(root), + projects, + root + ); if (plugin) { const main = readPluginMainFromProjectConfiguration( plugin.projectConfig @@ -203,6 +237,7 @@ function getPluginPathAndName( export async function loadNxPluginAsync( pluginConfiguration: PluginConfiguration, paths: string[], + projects: Record, root: string ): Promise { const { plugin: moduleName, options } = @@ -214,7 +249,12 @@ export async function loadNxPluginAsync( return { plugin: pluginModule, options }; } - let { pluginPath, name } = getPluginPathAndName(moduleName, paths, root); + let { pluginPath, name } = await getPluginPathAndName( + moduleName, + paths, + projects, + root + ); const plugin = ensurePluginIsV2( (await import(pluginPath)) as LoadedNxPlugin['plugin'] ); @@ -223,79 +263,21 @@ export async function loadNxPluginAsync( return { plugin, options }; } -function loadNxPluginSync( - pluginConfiguration: PluginConfiguration, - paths: string[], - root: string -): LoadedNxPlugin { - const { plugin: moduleName, options } = - typeof pluginConfiguration === 'object' - ? pluginConfiguration - : { plugin: pluginConfiguration, options: undefined }; - let pluginModule = nxPluginCache.get(moduleName); - if (pluginModule) { - return { plugin: pluginModule, options }; - } - - let { pluginPath, name } = getPluginPathAndName(moduleName, paths, root); - const plugin = ensurePluginIsV2( - require(pluginPath) - ) as LoadedNxPlugin['plugin']; - plugin.name ??= name; - nxPluginCache.set(moduleName, plugin); - return { plugin, options }; -} - -/** - * @deprecated Use loadNxPlugins instead. - */ -export function loadNxPluginsSync( - plugins: NxJsonConfiguration['plugins'], - paths = getNxRequirePaths(), - root = workspaceRoot -): LoadedNxPlugin[] { - // TODO: This should be specified in nx.json - // Temporarily load js as if it were a plugin which is built into nx - // In the future, this will be optional and need to be specified in nx.json - const result: LoadedNxPlugin[] = [...getDefaultPluginsSync(root)]; - - if (shouldMergeAngularProjects(root, false)) { - result.push({ plugin: NxAngularJsonPlugin, options: undefined }); - } - - plugins ??= []; - for (const plugin of plugins) { - try { - result.push(loadNxPluginSync(plugin, paths, root)); - } catch (e) { - if (e.code === 'ERR_REQUIRE_ESM') { - throw new Error( - `Unable to load "${plugin}". Plugins cannot be ESM modules. They must be CommonJS modules. Follow the issue on github: https://github.com/nrwl/nx/issues/15682` - ); - } - throw e; - } - } - - // We push the nx core node plugins onto the end, s.t. it overwrites any other plugins - result.push( - { plugin: getNxPackageJsonWorkspacesPlugin(root) }, - { plugin: CreateProjectJsonProjectsPlugin } - ); - - return result; -} - export async function loadNxPlugins( plugins: PluginConfiguration[], paths = getNxRequirePaths(), - root = workspaceRoot + root = workspaceRoot, + projects?: Record ): Promise { const result: LoadedNxPlugin[] = [...(await getDefaultPlugins(root))]; + // When loading plugins for `createNodes`, we don't know what projects exist yet. + projects ??= await retrieveProjectConfigurationsWithoutPluginInference(root); + plugins ??= []; + for (const plugin of plugins) { - result.push(await loadNxPluginAsync(plugin, paths, root)); + result.push(await loadNxPluginAsync(plugin, paths, projects, root)); } // We push the nx core node plugins onto the end, s.t. it overwrites any other plugins @@ -307,7 +289,7 @@ export async function loadNxPlugins( return result; } -function ensurePluginIsV2(plugin: NxPlugin): NxPluginV2 { +export function ensurePluginIsV2(plugin: NxPlugin): NxPluginV2 { if (isNxPluginV2(plugin)) { return plugin; } @@ -344,6 +326,7 @@ export function isNxPluginV1(plugin: NxPlugin): plugin is NxPluginV1 { export function readPluginPackageJson( pluginName: string, + projects: Record, paths = getNxRequirePaths() ): { path: string; @@ -357,7 +340,12 @@ export function readPluginPackageJson( }; } catch (e) { if (e.code === 'MODULE_NOT_FOUND') { - const localPluginPath = resolveLocalNxPlugin(pluginName); + const nxJson = readNxJson(); + const localPluginPath = resolveLocalNxPlugin( + pluginName, + nxJson, + projects + ); if (localPluginPath) { const localPluginPackageJson = path.join( localPluginPath.path, @@ -382,15 +370,23 @@ const localPluginCache: Record< string, { path: string; projectConfig: ProjectConfiguration } > = {}; + export function resolveLocalNxPlugin( importPath: string, + nxJsonConfiguration: NxJsonConfiguration, + projects: Record, root = workspaceRoot ): { path: string; projectConfig: ProjectConfiguration } | null { - localPluginCache[importPath] ??= lookupLocalPlugin(importPath, root); + localPluginCache[importPath] ??= lookupLocalPlugin( + importPath, + nxJsonConfiguration, + projects, + root + ); return localPluginCache[importPath]; } -let tsNodeAndPathsUnregisterCallback = undefined; +let tsNodeAndPathsUnregisterCallback: (() => void) | undefined = undefined; /** * Register swc-node or ts-node if they are not currently registered @@ -434,8 +430,12 @@ export function unregisterPluginTSTranspiler() { } } -function lookupLocalPlugin(importPath: string, root = workspaceRoot) { - const projects = retrieveProjectConfigurationsWithoutPluginInference(root); +function lookupLocalPlugin( + importPath: string, + nxJsonConfiguration: NxJsonConfiguration, + projects: Record, + root = workspaceRoot +) { const plugin = findNxProjectForImportPath(importPath, projects, root); if (!plugin) { return null; @@ -531,15 +531,4 @@ async function getDefaultPlugins(root: string): Promise { })); } -function getDefaultPluginsSync(root: string): LoadedNxPlugin[] { - const plugins: NxPluginV2[] = [require('../plugins/js')]; - - if (shouldMergeAngularProjects(root, false)) { - plugins.push(require('../adapter/angular-json').NxAngularJsonPlugin); - } - return plugins.map((p) => ({ - plugin: p, - })); -} - type Optional = Omit & Partial>; diff --git a/packages/nx/src/utils/plugins/installed-plugins.ts b/packages/nx/src/utils/plugins/installed-plugins.ts index aa040595f21278..f03aba7bcebaee 100644 --- a/packages/nx/src/utils/plugins/installed-plugins.ts +++ b/packages/nx/src/utils/plugins/installed-plugins.ts @@ -7,8 +7,9 @@ import { readJsonFile } from '../fileutils'; import { PackageJson, readModulePackageJson } from '../package-json'; import { workspaceRoot } from '../workspace-root'; import { join } from 'path'; -import { NxJsonConfiguration, readNxJson } from '../../config/nx-json'; +import { readNxJson } from '../../config/nx-json'; import { getNxRequirePaths } from '../installation-directory'; +import { ProjectConfiguration } from '../../config/workspace-json-project-json'; export function findInstalledPlugins(): PackageJson[] { const packageJsonDeps = getDependenciesFromPackageJson(); @@ -64,14 +65,19 @@ function getDependenciesFromNxJson(): string[] { } export async function getInstalledPluginsAndCapabilities( - workspaceRoot: string + workspaceRoot: string, + projects: Record ): Promise> { const plugins = findInstalledPlugins().map((p) => p.name); const result = new Map(); for (const plugin of Array.from(plugins).sort()) { try { - const capabilities = await getPluginCapabilities(workspaceRoot, plugin); + const capabilities = await getPluginCapabilities( + workspaceRoot, + plugin, + projects + ); if ( capabilities && (capabilities.executors || diff --git a/packages/nx/src/utils/plugins/local-plugins.ts b/packages/nx/src/utils/plugins/local-plugins.ts index ad601873a04e84..43cf7f9bd529bb 100644 --- a/packages/nx/src/utils/plugins/local-plugins.ts +++ b/packages/nx/src/utils/plugins/local-plugins.ts @@ -26,6 +26,7 @@ export async function getLocalWorkspacePlugins( const capabilities = await getPluginCapabilities( workspaceRoot, packageJson.name, + projectsConfiguration.projects, includeRuntimeCapabilities ); if ( diff --git a/packages/nx/src/utils/plugins/plugin-capabilities.ts b/packages/nx/src/utils/plugins/plugin-capabilities.ts index 08252297ec3ee0..156cc111415b88 100644 --- a/packages/nx/src/utils/plugins/plugin-capabilities.ts +++ b/packages/nx/src/utils/plugins/plugin-capabilities.ts @@ -13,6 +13,7 @@ import { } from '../nx-plugin'; import { getNxRequirePaths } from '../installation-directory'; import { PackageJson } from '../package-json'; +import { ProjectConfiguration } from '../../config/workspace-json-project-json'; function tryGetCollection( packageJsonPath: string, @@ -34,15 +35,18 @@ function tryGetCollection( export async function getPluginCapabilities( workspaceRoot: string, pluginName: string, + projects: Record, includeRuntimeCapabilities = false ): Promise { try { - const { json: packageJson, path: packageJsonPath } = readPluginPackageJson( - pluginName, - getNxRequirePaths(workspaceRoot) - ); + const { json: packageJson, path: packageJsonPath } = + await readPluginPackageJson( + pluginName, + projects, + getNxRequirePaths(workspaceRoot) + ); const pluginModule = includeRuntimeCapabilities - ? await tryGetModule(packageJson, workspaceRoot) + ? await tryGetModule(packageJson, workspaceRoot, projects) : ({} as Record); return { name: pluginName, @@ -95,7 +99,8 @@ export async function getPluginCapabilities( async function tryGetModule( packageJson: PackageJson, - workspaceRoot: string + workspaceRoot: string, + projects: Record ): Promise { try { return packageJson.generators ?? @@ -107,6 +112,7 @@ async function tryGetModule( await loadNxPluginAsync( packageJson.name, getNxRequirePaths(workspaceRoot), + projects, workspaceRoot ) ).plugin @@ -118,8 +124,15 @@ async function tryGetModule( } } -export async function listPluginCapabilities(pluginName: string) { - const plugin = await getPluginCapabilities(workspaceRoot, pluginName); +export async function listPluginCapabilities( + pluginName: string, + projects: Record +) { + const plugin = await getPluginCapabilities( + workspaceRoot, + pluginName, + projects + ); if (!plugin) { const pmc = getPackageManagerCommand(); diff --git a/packages/nx/src/utils/workspace-context.ts b/packages/nx/src/utils/workspace-context.ts index 57dc8a91ae2332..73bf6bbbaf131f 100644 --- a/packages/nx/src/utils/workspace-context.ts +++ b/packages/nx/src/utils/workspace-context.ts @@ -19,7 +19,7 @@ export function setupWorkspaceContext(workspaceRoot: string) { export function getNxWorkspaceFilesFromContext( workspaceRoot: string, globs: string[], - parseConfigurations: (files: string[]) => Record + parseConfigurations: (files: string[]) => Promise> ) { ensureContextAvailable(workspaceRoot); return workspaceContext.getWorkspaceFiles(globs, parseConfigurations); @@ -36,7 +36,7 @@ export function globWithWorkspaceContext( export function getProjectConfigurationsFromContext( workspaceRoot: string, globs: string[], - parseConfigurations: (files: string[]) => Record + parseConfigurations: (files: string[]) => Promise> ) { ensureContextAvailable(workspaceRoot); return workspaceContext.getProjectConfigurations(globs, parseConfigurations);