diff --git a/packages/nx/bin/post-install.ts b/packages/nx/bin/post-install.ts index 714386af0aa0a1..9822b4575e8610 100644 --- a/packages/nx/bin/post-install.ts +++ b/packages/nx/bin/post-install.ts @@ -17,9 +17,11 @@ import { setupWorkspaceContext } from '../src/utils/workspace-context'; if (isMainNxPackage() && fileExists(join(workspaceRoot, 'nx.json'))) { assertSupportedPlatform(); setupWorkspaceContext(workspaceRoot); - try { - await daemonClient.stop(); - } catch (e) {} + if (daemonClient.enabled()) { + try { + await daemonClient.stop(); + } catch (e) {} + } const tasks: Array> = [ buildProjectGraphAndSourceMapsWithoutDaemon(), ]; diff --git a/packages/nx/plugins/package-json.ts b/packages/nx/plugins/package-json.ts index 502e0da2502047..3ea2d5a85e6273 100644 --- a/packages/nx/plugins/package-json.ts +++ b/packages/nx/plugins/package-json.ts @@ -1,13 +1,50 @@ -import type { NxPluginV2 } from '../src/project-graph/plugins'; +import { createNodesFromFiles, NxPluginV2 } from '../src/project-graph/plugins'; import { workspaceRoot } from '../src/utils/workspace-root'; import { createNodeFromPackageJson } from '../src/plugins/package-json'; +import { workspaceDataDirectory } from '../src/utils/cache-directory'; +import { join } from 'path'; +import { ProjectConfiguration } from '../src/config/workspace-json-project-json'; +import { readJsonFile, writeJsonFile } from '../src/utils/fileutils'; + +export type PackageJsonConfigurationCache = { + [hash: string]: ProjectConfiguration; +}; + +const cachePath = join(workspaceDataDirectory, 'package-json.hash'); + +export function readPackageJsonConfigurationCache() { + try { + return readJsonFile(cachePath); + } catch (e) { + return {}; + } +} + +function writeCache(cache: PackageJsonConfigurationCache) { + writeJsonFile(cachePath, cache); +} const plugin: NxPluginV2 = { name: 'nx-all-package-jsons-plugin', - createNodes: [ + createNodesV2: [ '*/**/package.json', - (f) => createNodeFromPackageJson(f, workspaceRoot), + (configFiles, options, context) => { + const cache = readPackageJsonConfigurationCache(); + + const result = createNodesFromFiles( + (f) => createNodeFromPackageJson(f, workspaceRoot, cache), + configFiles, + options, + context + ); + + writeCache(cache); + + return result; + }, ], }; module.exports = plugin; +module.exports.readPackageJsonConfigurationCache = + readPackageJsonConfigurationCache; diff --git a/packages/nx/src/command-line/affected/affected.ts b/packages/nx/src/command-line/affected/affected.ts index 2bda67751f240b..acc52aebb6ae25 100644 --- a/packages/nx/src/command-line/affected/affected.ts +++ b/packages/nx/src/command-line/affected/affected.ts @@ -9,7 +9,10 @@ import { splitArgsIntoNxArgsAndOverrides, } from '../../utils/command-line-utils'; import { performance } from 'perf_hooks'; -import { createProjectGraphAsync } from '../../project-graph/project-graph'; +import { + createProjectGraphAndSourceMapsAsync, + createProjectGraphAsync, +} from '../../project-graph/project-graph'; import { ProjectGraph, ProjectGraphProjectNode, @@ -21,6 +24,7 @@ import { readNxJson } from '../../config/configuration'; import { findMatchingProjects } from '../../utils/find-matching-projects'; import { generateGraph } from '../graph/graph'; import { allFileData } from '../../utils/all-file-data'; +import { ConfigurationSourceMaps } from '../../project-graph/utils/project-configuration-utils'; export async function affected( command: 'graph' | 'print-affected' | 'affected', @@ -53,8 +57,15 @@ export async function affected( await connectToNxCloudIfExplicitlyAsked(nxArgs); - const projectGraph = await createProjectGraphAsync({ exitOnError: true }); - const projects = await getAffectedGraphNodes(nxArgs, projectGraph); + const { projectGraph, sourceMaps } = + await createProjectGraphAndSourceMapsAsync({ + exitOnError: true, + }); + const projects = await getAffectedGraphNodes( + nxArgs, + projectGraph, + sourceMaps + ); try { switch (command) { @@ -100,12 +111,14 @@ export async function affected( export async function getAffectedGraphNodes( nxArgs: NxArgs, - projectGraph: ProjectGraph + projectGraph: ProjectGraph, + sourceMaps: ConfigurationSourceMaps ): Promise { let affectedGraph = nxArgs.all ? projectGraph : await filterAffected( projectGraph, + sourceMaps, calculateFileChanges( parseFiles(nxArgs).files, await allFileData(), diff --git a/packages/nx/src/command-line/format/format.ts b/packages/nx/src/command-line/format/format.ts index c5a9c028a7ac43..d0abe22889836a 100644 --- a/packages/nx/src/command-line/format/format.ts +++ b/packages/nx/src/command-line/format/format.ts @@ -19,7 +19,10 @@ import { getRootTsConfigPath, } from '../../plugins/js/utils/typescript'; import { filterAffected } from '../../project-graph/affected/affected-project-graph'; -import { createProjectGraphAsync } from '../../project-graph/project-graph'; +import { + createProjectGraphAndSourceMapsAsync, + createProjectGraphAsync, +} from '../../project-graph/project-graph'; import { allFileData } from '../../utils/all-file-data'; import { chunkify } from '../../utils/chunkify'; import { sortObjectByKeys } from '../../utils/object-sort'; @@ -156,9 +159,13 @@ async function getPatternsFromApps( allWorkspaceFiles: FileData[], projectGraph: ProjectGraph ): Promise { - const graph = await createProjectGraphAsync({ exitOnError: true }); + const { projectGraph: graph, sourceMaps } = + await createProjectGraphAndSourceMapsAsync({ + exitOnError: true, + }); const affectedGraph = await filterAffected( graph, + sourceMaps, calculateFileChanges(affectedFiles, allWorkspaceFiles) ); return getPatternsFromProjects( diff --git a/packages/nx/src/command-line/graph/graph.ts b/packages/nx/src/command-line/graph/graph.ts index e355d7d137682b..e38b8951661bd3 100644 --- a/packages/nx/src/command-line/graph/graph.ts +++ b/packages/nx/src/command-line/graph/graph.ts @@ -355,7 +355,8 @@ export async function generateGraph( { printWarnings: args.file !== 'stdout' }, readNxJson() ).nxArgs, - rawGraph + rawGraph, + sourceMaps ) ).map((n) => n.name); } diff --git a/packages/nx/src/command-line/show/projects.ts b/packages/nx/src/command-line/show/projects.ts index 1da055b751ee3b..403504d5624494 100644 --- a/packages/nx/src/command-line/show/projects.ts +++ b/packages/nx/src/command-line/show/projects.ts @@ -10,7 +10,10 @@ import { calculateFileChanges, } from '../../project-graph/file-utils'; import { filterNodes } from '../../project-graph/operators'; -import { createProjectGraphAsync } from '../../project-graph/project-graph'; +import { + createProjectGraphAndSourceMapsAsync, + createProjectGraphAsync, +} from '../../project-graph/project-graph'; import { allFileData } from '../../utils/all-file-data'; import { NxArgs, @@ -19,11 +22,13 @@ import { } from '../../utils/command-line-utils'; import { findMatchingProjects } from '../../utils/find-matching-projects'; import { ShowProjectsOptions } from './command-object'; +import { ConfigurationSourceMaps } from '../../project-graph/utils/project-configuration-utils'; export async function showProjectsHandler( args: ShowProjectsOptions ): Promise { - let graph = await createProjectGraphAsync(); + let { projectGraph: graph, sourceMaps } = + await createProjectGraphAndSourceMapsAsync(); const nxJson = readNxJson(); const { nxArgs } = splitArgsIntoNxArgsAndOverrides( args, @@ -37,7 +42,7 @@ export async function showProjectsHandler( // Affected touches dependencies so it needs to be processed first. if (args.affected) { const touchedFiles = await getTouchedFiles(nxArgs); - graph = await getAffectedGraph(touchedFiles, nxJson, graph); + graph = await getAffectedGraph(touchedFiles, nxJson, graph, sourceMaps); } const filter = filterNodes((node) => { @@ -100,9 +105,10 @@ function getGraphNodesMatchingPatterns( function getAffectedGraph( touchedFiles: FileChange[], nxJson: NxJsonConfiguration<'*' | string[]>, - graph: ProjectGraph + graph: ProjectGraph, + sourceMaps: ConfigurationSourceMaps ) { - return filterAffected(graph, touchedFiles, nxJson); + return filterAffected(graph, sourceMaps, touchedFiles, nxJson); } async function getTouchedFiles(nxArgs: NxArgs): Promise { diff --git a/packages/nx/src/daemon/server/handle-request-project-graph.ts b/packages/nx/src/daemon/server/handle-request-project-graph.ts index 1dc4507383c82a..449d57a6938052 100644 --- a/packages/nx/src/daemon/server/handle-request-project-graph.ts +++ b/packages/nx/src/daemon/server/handle-request-project-graph.ts @@ -3,8 +3,6 @@ import { serializeResult } from '../socket-utils'; import { serverLogger } from './logger'; import { getCachedSerializedProjectGraphPromise } from './project-graph-incremental-recomputation'; import { HandlerResult } from './server'; -import { getPlugins } from './plugins'; -import { readNxJson } from '../../config/nx-json'; export async function handleRequestProjectGraph(): Promise { try { diff --git a/packages/nx/src/daemon/server/project-graph-incremental-recomputation.ts b/packages/nx/src/daemon/server/project-graph-incremental-recomputation.ts index fc3abef69a4389..8e8bfe71f40806 100644 --- a/packages/nx/src/daemon/server/project-graph-incremental-recomputation.ts +++ b/packages/nx/src/daemon/server/project-graph-incremental-recomputation.ts @@ -31,12 +31,12 @@ import { serverLogger } from './logger'; import { NxWorkspaceFilesExternals } from '../../native'; import { ConfigurationResult } from '../../project-graph/utils/project-configuration-utils'; import { LoadedNxPlugin } from '../../project-graph/plugins/internal-api'; -import { getPlugins } from './plugins'; import { DaemonProjectGraphError, ProjectConfigurationsError, isAggregateProjectGraphError, } from '../../project-graph/error-types'; +import { getPlugins } from '../../project-graph/plugins'; interface SerializedProjectGraph { error: Error | null; diff --git a/packages/nx/src/daemon/server/shutdown-utils.ts b/packages/nx/src/daemon/server/shutdown-utils.ts index fde1e65d936f60..415ca3b92250e3 100644 --- a/packages/nx/src/daemon/server/shutdown-utils.ts +++ b/packages/nx/src/daemon/server/shutdown-utils.ts @@ -4,12 +4,12 @@ import { serverLogger } from './logger'; import { serializeResult } from '../socket-utils'; import { deleteDaemonJsonProcessCache } from '../cache'; import type { Watcher } from '../../native'; -import { cleanupPlugins } from './plugins'; import { DaemonProjectGraphError, ProjectGraphError, } from '../../project-graph/error-types'; import { removeDbConnections } from '../../utils/db-connection'; +import { cleanupPlugins } from '../../project-graph/plugins/plugins'; export const SERVER_INACTIVITY_TIMEOUT_MS = 10800000 as const; // 10800000 ms = 3 hours diff --git a/packages/nx/src/executors/utils/convert-nx-executor.ts b/packages/nx/src/executors/utils/convert-nx-executor.ts index 5a43944bfdc0df..dae1dcdcb945de 100644 --- a/packages/nx/src/executors/utils/convert-nx-executor.ts +++ b/packages/nx/src/executors/utils/convert-nx-executor.ts @@ -8,7 +8,7 @@ import { Executor, ExecutorContext } from '../../config/misc-interfaces'; import { retrieveProjectConfigurations } from '../../project-graph/utils/retrieve-workspace-files'; import { readProjectConfigurationsFromRootMap } from '../../project-graph/utils/project-configuration-utils'; import { ProjectsConfigurations } from '../../config/workspace-json-project-json'; -import { loadNxPlugins } from '../../project-graph/plugins/internal-api'; +import { getPlugins } from '../../project-graph/plugins'; /** * Convert an Nx Executor into an Angular Devkit Builder @@ -20,10 +20,7 @@ export function convertNxExecutor(executor: Executor) { const promise = async () => { const nxJsonConfiguration = readNxJson(builderContext.workspaceRoot); - const [plugins, cleanup] = await loadNxPlugins( - nxJsonConfiguration.plugins, - builderContext.workspaceRoot - ); + const plugins = await getPlugins(); const projectsConfigurations: ProjectsConfigurations = { version: 2, projects: readProjectConfigurationsFromRootMap( @@ -36,7 +33,6 @@ export function convertNxExecutor(executor: Executor) { ).projects ), }; - cleanup(); const context: ExecutorContext = { root: builderContext.workspaceRoot, projectName: builderContext.target.project, diff --git a/packages/nx/src/plugins/package-json/create-nodes.spec.ts b/packages/nx/src/plugins/package-json/create-nodes.spec.ts index fa6bdda6d70db8..a83cf0a1830881 100644 --- a/packages/nx/src/plugins/package-json/create-nodes.spec.ts +++ b/packages/nx/src/plugins/package-json/create-nodes.spec.ts @@ -48,7 +48,7 @@ describe('nx package.json workspaces plugin', () => { '/root' ); - expect(createNodeFromPackageJson('package.json', '/root')) + expect(createNodeFromPackageJson('package.json', '/root', {})) .toMatchInlineSnapshot(` { "projects": { @@ -90,8 +90,9 @@ describe('nx package.json workspaces plugin', () => { }, } `); - expect(createNodeFromPackageJson('packages/lib-a/package.json', '/root')) - .toMatchInlineSnapshot(` + expect( + createNodeFromPackageJson('packages/lib-a/package.json', '/root', {}) + ).toMatchInlineSnapshot(` { "projects": { "packages/lib-a": { @@ -132,8 +133,9 @@ describe('nx package.json workspaces plugin', () => { }, } `); - expect(createNodeFromPackageJson('packages/lib-b/package.json', '/root')) - .toMatchInlineSnapshot(` + expect( + createNodeFromPackageJson('packages/lib-b/package.json', '/root', {}) + ).toMatchInlineSnapshot(` { "projects": { "packages/lib-b": { @@ -731,13 +733,12 @@ describe('nx package.json workspaces plugin', () => { ); expect( - createNodeFromPackageJson('apps/myapp/package.json', '/root').projects[ - 'apps/myapp' - ].projectType + createNodeFromPackageJson('apps/myapp/package.json', '/root', {}) + .projects['apps/myapp'].projectType ).toEqual('application'); expect( - createNodeFromPackageJson('packages/mylib/package.json', '/root') + createNodeFromPackageJson('packages/mylib/package.json', '/root', {}) .projects['packages/mylib'].projectType ).toEqual('library'); }); @@ -760,7 +761,7 @@ describe('nx package.json workspaces plugin', () => { ); expect( - createNodeFromPackageJson('package.json', '/root').projects['.'] + createNodeFromPackageJson('package.json', '/root', {}).projects['.'] .projectType ).toEqual('library'); }); @@ -786,11 +787,11 @@ describe('nx package.json workspaces plugin', () => { ); expect( - createNodeFromPackageJson('packages/mylib/package.json', '/root') + createNodeFromPackageJson('packages/mylib/package.json', '/root', {}) .projects['packages/mylib'].projectType ).toEqual('library'); expect( - createNodeFromPackageJson('example/package.json', '/root').projects[ + createNodeFromPackageJson('example/package.json', '/root', {}).projects[ 'example' ].projectType ).toBeUndefined(); diff --git a/packages/nx/src/plugins/package-json/create-nodes.ts b/packages/nx/src/plugins/package-json/create-nodes.ts index 0670dffcd7b137..1a4bdea2e3c590 100644 --- a/packages/nx/src/plugins/package-json/create-nodes.ts +++ b/packages/nx/src/plugins/package-json/create-nodes.ts @@ -21,6 +21,11 @@ import { CreateNodesV2, } from '../../project-graph/plugins'; import { basename } from 'path'; +import { hashObject } from '../../hasher/file-hasher'; +import { + PackageJsonConfigurationCache, + readPackageJsonConfigurationCache, +} from '../../../plugins/package-json'; export const createNodesV2: CreateNodesV2 = [ combineGlobPatterns( @@ -41,6 +46,8 @@ export const createNodesV2: CreateNodesV2 = [ return projectJsonRoots.has(dirname(packageJsonPath)); }; + const cache = readPackageJsonConfigurationCache(); + return createNodesFromFiles( (packageJsonPath, options, context) => { if ( @@ -53,7 +60,8 @@ export const createNodesV2: CreateNodesV2 = [ return createNodeFromPackageJson( packageJsonPath, - context.workspaceRoot + context.workspaceRoot, + cache ); }, packageJsons, @@ -120,15 +128,35 @@ export function buildPackageJsonWorkspacesMatcher( export function createNodeFromPackageJson( pkgJsonPath: string, - workspaceRoot: string + workspaceRoot: string, + cache: PackageJsonConfigurationCache ) { const json: PackageJson = readJsonFile(join(workspaceRoot, pkgJsonPath)); + + const projectRoot = dirname(pkgJsonPath); + + const hash = hashObject({ + ...json, + root: projectRoot, + }); + + const cached = cache[hash]; + if (cached) { + return { + projects: { + [cached.root]: cached, + }, + }; + } + const project = buildProjectConfigurationFromPackageJson( json, workspaceRoot, pkgJsonPath, readNxJson(workspaceRoot) ); + + cache[hash] = project; return { projects: { [project.root]: project, diff --git a/packages/nx/src/project-graph/affected/affected-project-graph-models.ts b/packages/nx/src/project-graph/affected/affected-project-graph-models.ts index 5870c7c4e05184..2064389c221fbe 100644 --- a/packages/nx/src/project-graph/affected/affected-project-graph-models.ts +++ b/packages/nx/src/project-graph/affected/affected-project-graph-models.ts @@ -4,6 +4,7 @@ import { ProjectGraph, ProjectGraphProjectNode, } from '../../config/project-graph'; +import { ConfigurationSourceMaps } from '../utils/project-configuration-utils'; export interface AffectedProjectGraphContext { projectGraphNodes: Record; @@ -17,6 +18,7 @@ export interface TouchedProjectLocator { projectGraphNodes?: Record, nxJson?: NxJsonConfiguration, packageJson?: any, - projectGraph?: ProjectGraph + projectGraph?: ProjectGraph, + sourceMap?: ConfigurationSourceMaps ): string[] | Promise; } diff --git a/packages/nx/src/project-graph/affected/affected-project-graph.ts b/packages/nx/src/project-graph/affected/affected-project-graph.ts index 52cc5c8f0f2188..97e0f51cf132ec 100644 --- a/packages/nx/src/project-graph/affected/affected-project-graph.ts +++ b/packages/nx/src/project-graph/affected/affected-project-graph.ts @@ -13,9 +13,11 @@ import { ProjectGraph } from '../../config/project-graph'; import { reverse } from '../operators'; import { readNxJson } from '../../config/configuration'; import { getTouchedProjectsFromProjectGlobChanges } from './locators/project-glob-changes'; +import { ConfigurationSourceMaps } from '../utils/project-configuration-utils'; export async function filterAffected( graph: ProjectGraph, + sourceMap: ConfigurationSourceMaps, touchedFiles: FileChange[], nxJson: NxJsonConfiguration = readNxJson(), packageJson: any = readPackageJson() @@ -35,7 +37,8 @@ export async function filterAffected( graph.nodes, nxJson, packageJson, - graph + graph, + sourceMap ); touchedProjects.push(...projects); } diff --git a/packages/nx/src/project-graph/affected/locators/project-glob-changes.spec.ts b/packages/nx/src/project-graph/affected/locators/project-glob-changes.spec.ts index 31578fd73cb109..ff0f5fd070f992 100644 --- a/packages/nx/src/project-graph/affected/locators/project-glob-changes.spec.ts +++ b/packages/nx/src/project-graph/affected/locators/project-glob-changes.spec.ts @@ -1,55 +1,67 @@ -import { ProjectGraphProjectNode } from '../../../config/project-graph'; -import { ProjectConfiguration } from '../../../config/workspace-json-project-json'; - import * as nxPlugin from '../../../project-graph/plugins'; +import { ProjectGraphProjectNode } from '../../../config/project-graph'; import { DeletedFileChange } from '../../file-utils'; import { getTouchedProjectsFromProjectGlobChanges } from './project-glob-changes'; describe('getTouchedProjectsFromProjectGlobChanges', () => { - it('empty', () => {}); + beforeEach(() => { + jest.spyOn(nxPlugin, 'getPlugins').mockResolvedValue([ + { + name: 'test', + createNodes: [ + '**/project.json', + async () => { + return []; + }, + ], + }, + ]); + }); + it('should affect all projects if a project is removed', async () => { + const nodes = { + proj1: makeProjectGraphNode('proj1'), + proj2: makeProjectGraphNode('proj2'), + proj3: makeProjectGraphNode('proj3'), + }; + const result = await getTouchedProjectsFromProjectGlobChanges( + [ + { + file: 'libs/proj1/project.json', + hash: 'some-hash', + getChanges: () => [new DeletedFileChange()], + }, + ], + nodes, + { + plugins: [], + }, + {}, + { + nodes: nodes, + dependencies: {}, + }, + { + 'libs/proj1': { + root: ['libs/proj1/project.json', 'some-plugin'], + }, + 'libs/proj2': { + root: ['libs/proj2/project.json', 'some-plugin'], + }, + 'libs/proj3': { + root: ['libs/proj3/project.json', 'some-plugin'], + }, + } + ); + expect(result).toEqual(['proj1', 'proj2', 'proj3']); + }); }); -// describe('getTouchedProjectsFromProjectGlobChanges', () => { -// beforeEach(() => { -// jest.spyOn(nxPlugin, 'loadNxPlugins').mockResolvedValue([]); -// }); -// -// it('should affect all projects if a project is removed', async () => { -// const result = await getTouchedProjectsFromProjectGlobChanges( -// [ -// { -// file: 'libs/proj1/project.json', -// hash: 'some-hash', -// getChanges: () => [new DeletedFileChange()], -// }, -// ], -// { -// proj2: makeProjectGraphNode('proj2'), -// proj3: makeProjectGraphNode('proj3'), -// }, -// { -// plugins: [], -// } -// ); -// expect(result).toEqual(['proj2', 'proj3']); -// }); -// }); - -// function makeProjectGraphNode( -// name, -// configurationFile = 'project.json' -// ): ProjectGraphProjectNode { -// return { -// data: { -// files: [ -// { -// file: `libs/${name}/${configurationFile}`, -// hash: 'hash' + Math.floor(Math.random() * 10000), -// }, -// ], -// root: `libs/${name}`, -// }, -// name, -// type: 'lib', -// }; -// } +function makeProjectGraphNode(name): ProjectGraphProjectNode { + return { + data: { + root: `libs/${name}`, + }, + name, + type: 'lib', + }; +} diff --git a/packages/nx/src/project-graph/affected/locators/project-glob-changes.ts b/packages/nx/src/project-graph/affected/locators/project-glob-changes.ts index 6c54137366a25f..3a22e391bb2c75 100644 --- a/packages/nx/src/project-graph/affected/locators/project-glob-changes.ts +++ b/packages/nx/src/project-graph/affected/locators/project-glob-changes.ts @@ -4,12 +4,12 @@ import { workspaceRoot } from '../../../utils/workspace-root'; import { join } from 'path'; import { existsSync } from 'fs'; import { configurationGlobs } from '../../utils/retrieve-workspace-files'; -import { loadNxPlugins } from '../../plugins/internal-api'; import { combineGlobPatterns } from '../../../utils/globs'; +import { getPlugins } from '../../plugins'; export const getTouchedProjectsFromProjectGlobChanges: TouchedProjectLocator = async (touchedFiles, projectGraphNodes, nxJson): Promise => { - const [plugins] = await loadNxPlugins(nxJson?.plugins ?? [], workspaceRoot); + const plugins = await getPlugins(); const globPattern = combineGlobPatterns(configurationGlobs(plugins)); const touchedProjects = new Set(); diff --git a/packages/nx/src/daemon/server/plugins.ts b/packages/nx/src/project-graph/plugins/plugins.ts similarity index 58% rename from packages/nx/src/daemon/server/plugins.ts rename to packages/nx/src/project-graph/plugins/plugins.ts index 3e6da219c98a8a..bec30b7b9f5958 100644 --- a/packages/nx/src/daemon/server/plugins.ts +++ b/packages/nx/src/project-graph/plugins/plugins.ts @@ -37,6 +37,34 @@ export async function getPlugins() { return result; } +let loadedDefaultPlugins: LoadedNxPlugin[]; +let cleanupDefaultPlugins: () => void; + +export async function getOnlyDefaultPlugins() { + const pluginsConfiguration = readNxJson().plugins ?? []; + const pluginsConfigurationHash = hashObject(pluginsConfiguration); + + // If the plugins configuration has not changed, reuse the current plugins + if (loadedDefaultPlugins) { + return loadedPlugins; + } + + // Cleanup current plugins before loading new ones + if (cleanupDefaultPlugins) { + cleanupDefaultPlugins(); + } + + currentPluginsConfigurationHash = pluginsConfigurationHash; + const [result, cleanupFn] = await loadNxPlugins( + pluginsConfiguration, + workspaceRoot + ); + cleanupDefaultPlugins = cleanupFn; + loadedPlugins = result; + return result; +} + export function cleanupPlugins() { cleanup(); + cleanupDefaultPlugins(); } diff --git a/packages/nx/src/project-graph/plugins/public-api.ts b/packages/nx/src/project-graph/plugins/public-api.ts index 7049a98f7e8a6a..6077861de5d3a5 100644 --- a/packages/nx/src/project-graph/plugins/public-api.ts +++ b/packages/nx/src/project-graph/plugins/public-api.ts @@ -182,3 +182,5 @@ export type NxPluginV2 = { * A plugin for Nx */ export type NxPlugin = NxPluginV2; + +export * from './plugins'; diff --git a/packages/nx/src/project-graph/project-graph.ts b/packages/nx/src/project-graph/project-graph.ts index eb1c101faee7b2..f1e95e809e3492 100644 --- a/packages/nx/src/project-graph/project-graph.ts +++ b/packages/nx/src/project-graph/project-graph.ts @@ -24,12 +24,12 @@ import { readProjectGraphCache, writeCache, } from './nx-deps-cache'; -import { loadNxPlugins } from './plugins/internal-api'; import { ConfigurationResult } from './utils/project-configuration-utils'; import { retrieveProjectConfigurations, retrieveWorkspaceFiles, } from './utils/retrieve-workspace-files'; +import { getPlugins } from './plugins/plugins'; /** * Synchronously reads the latest cached copy of the workspace's ProjectGraph. @@ -96,7 +96,7 @@ export async function buildProjectGraphAndSourceMapsWithoutDaemon() { performance.mark('retrieve-project-configurations:start'); let configurationResult: ConfigurationResult; let projectConfigurationsError: ProjectConfigurationsError; - const [plugins, cleanup] = await loadNxPlugins(nxJson.plugins); + const plugins = await getPlugins(); try { configurationResult = await retrieveProjectConfigurations( plugins, @@ -147,14 +147,6 @@ export async function buildProjectGraphAndSourceMapsWithoutDaemon() { } else { throw e; } - } finally { - // When plugins are isolated we don't clean them up during - // a single run of the CLI. They are cleaned up when the CLI - // process exits. Cleaning them here could cause issues if pending - // promises are not resolved. - if (process.env.NX_ISOLATE_PLUGINS !== 'true') { - cleanup(); - } } const { projectGraph, projectFileMapCache } = projectGraphResult; 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 d87792f1093fc9..d5882f985895a6 100644 --- a/packages/nx/src/project-graph/utils/retrieve-workspace-files.ts +++ b/packages/nx/src/project-graph/utils/retrieve-workspace-files.ts @@ -9,13 +9,14 @@ import { ConfigurationResult, createProjectConfigurations, } from './project-configuration-utils'; -import { LoadedNxPlugin, loadNxPlugins } from '../plugins/internal-api'; +import { LoadedNxPlugin } from '../plugins/internal-api'; import { getNxWorkspaceFilesFromContext, globWithWorkspaceContext, } from '../../utils/workspace-context'; import { buildAllWorkspaceFiles } from './build-all-workspace-files'; import { join } from 'path'; +import { getOnlyDefaultPlugins, getPlugins } from '../plugins'; /** * Walks the workspace directory to create the `projectFileMap`, `ProjectConfigurations` and `allWorkspaceFiles` @@ -96,23 +97,19 @@ export async function retrieveProjectConfigurationsWithAngularProjects( pluginsToLoad.push(join(__dirname, '../../adapter/angular-json')); } - const [plugins, cleanup] = await loadNxPlugins( - nxJson?.plugins ?? [], - workspaceRoot - ); + const plugins = await getPlugins(); const res = await retrieveProjectConfigurations( plugins, workspaceRoot, nxJson ); - cleanup(); return res; } export function retrieveProjectConfigurationPaths( root: string, - plugins: Array<{ createNodes?: readonly [string, ...unknown[]] } & unknown> + plugins: Array ): Promise { const projectGlobPatterns = configurationGlobs(plugins); return globWithWorkspaceContext(root, projectGlobPatterns); @@ -128,7 +125,7 @@ export async function retrieveProjectConfigurationsWithoutPluginInference( root: string ): Promise> { const nxJson = readNxJson(root); - const [plugins, cleanup] = await loadNxPlugins([]); // only load default plugins + const plugins = await getOnlyDefaultPlugins(); // only load default plugins const projectGlobPatterns = await retrieveProjectConfigurationPaths( root, plugins @@ -150,14 +147,10 @@ export async function retrieveProjectConfigurationsWithoutPluginInference( projectsWithoutPluginCache.set(cacheKey, projects); - cleanup(); - return projects; } -export function configurationGlobs( - plugins: Array<{ createNodes?: readonly [string, ...unknown[]] }> -): string[] { +export function configurationGlobs(plugins: Array): string[] { const globPatterns = []; for (const plugin of plugins) { if ('createNodes' in plugin && plugin.createNodes) {