From 56289555fcc988c12174ee390a1689f33e9aee09 Mon Sep 17 00:00:00 2001 From: Craigory Coppola Date: Wed, 15 Feb 2023 16:29:31 -0500 Subject: [PATCH] feat(core): nx report should support encapsulated installs (#15032) --- e2e/nx-init/src/nx-init.test.ts | 62 --------------- e2e/nx-misc/src/encapsulated.test.ts | 84 +++++++++++++++++++++ packages/nx/src/command-line/report.spec.ts | 50 +++++++++++- packages/nx/src/command-line/report.ts | 39 +++++++--- 4 files changed, 163 insertions(+), 72 deletions(-) create mode 100644 e2e/nx-misc/src/encapsulated.test.ts diff --git a/e2e/nx-init/src/nx-init.test.ts b/e2e/nx-init/src/nx-init.test.ts index 29658d3ca2..159c81ff54 100644 --- a/e2e/nx-init/src/nx-init.test.ts +++ b/e2e/nx-init/src/nx-init.test.ts @@ -1,19 +1,13 @@ -import { NxJsonConfiguration } from '@nrwl/devkit'; import { - checkFilesDoNotExist, - checkFilesExist, cleanupProject, createNonNxProjectDirectory, getPackageManagerCommand, getPublishedVersion, getSelectedPackageManager, - newEncapsulatedNxWorkspace, - removeFile, renameFile, runCLI, runCommand, updateFile, - updateJson, } from '@nrwl/e2e/utils'; describe('nx init', () => { @@ -102,60 +96,4 @@ describe('nx init', () => { expect(output).not.toContain('HELLO COMPOUND'); cleanupProject(); }); - - describe('encapsulated', () => { - it('should support running targets in a encapsulated repo', () => { - const runEncapsulatedNx = newEncapsulatedNxWorkspace(); - updateFile( - 'projects/a/project.json', - JSON.stringify({ - name: 'a', - targets: { - echo: { - command: `echo 'Hello from A'`, - }, - }, - }) - ); - - updateJson('nx.json', (json) => ({ - ...json, - tasksRunnerOptions: { - default: { - ...json.tasksRunnerOptions['default'], - options: { - ...json.tasksRunnerOptions['default'].options, - cacheableOperations: ['echo'], - }, - }, - }, - })); - - expect(runEncapsulatedNx('echo a')).toContain('Hello from A'); - - expect(runEncapsulatedNx('echo a')).toContain( - 'Nx read the output from the cache instead of running the command for 1 out of 1 tasks' - ); - - expect(() => - checkFilesDoNotExist( - 'node_modules', - 'package.json', - 'package-lock.json', - 'yarn-lock.json', - 'pnpm-lock.yaml' - ) - ).not.toThrow(); - expect(() => - checkFilesExist( - '.nx/installation/package.json', - '.nx/installation/package-lock.json', - '.nx/cache/terminalOutputs' - ) - ).not.toThrow(); - }); - cleanupProject({ - skipReset: true, - }); - }); }); diff --git a/e2e/nx-misc/src/encapsulated.test.ts b/e2e/nx-misc/src/encapsulated.test.ts new file mode 100644 index 0000000000..fa1ddc6174 --- /dev/null +++ b/e2e/nx-misc/src/encapsulated.test.ts @@ -0,0 +1,84 @@ +import type { NxJsonConfiguration } from '@nrwl/devkit'; +import { + newEncapsulatedNxWorkspace, + updateFile, + updateJson, + checkFilesDoNotExist, + checkFilesExist, + cleanupProject, + getPublishedVersion, +} from '@nrwl/e2e/utils'; + +describe('encapsulated nx', () => { + let runEncapsulatedNx: ReturnType; + + beforeAll(() => { + runEncapsulatedNx = newEncapsulatedNxWorkspace(); + }); + + afterAll(() => { + cleanupProject({ + skipReset: true, + }); + }); + + it('should support running targets in a encapsulated repo', () => { + updateFile( + 'projects/a/project.json', + JSON.stringify({ + name: 'a', + targets: { + echo: { + command: `echo 'Hello from A'`, + }, + }, + }) + ); + + updateJson('nx.json', (json) => ({ + ...json, + tasksRunnerOptions: { + default: { + ...json.tasksRunnerOptions['default'], + options: { + ...json.tasksRunnerOptions['default'].options, + cacheableOperations: ['echo'], + }, + }, + }, + })); + + expect(runEncapsulatedNx('echo a')).toContain('Hello from A'); + + expect(runEncapsulatedNx('echo a')).toContain( + 'Nx read the output from the cache instead of running the command for 1 out of 1 tasks' + ); + + assertNoRootPackages(); + expect(() => + checkFilesExist( + '.nx/installation/package.json', + '.nx/installation/package-lock.json', + '.nx/cache/terminalOutputs' + ) + ).not.toThrow(); + }); + + it('should work with nx report', () => { + expect(runEncapsulatedNx('report')).toMatch( + new RegExp(`nx.*:.*${getPublishedVersion()}`) + ); + }); +}); + +function assertNoRootPackages() { + expect(() => + checkFilesDoNotExist( + 'node_modules', + 'package.json', + 'package-lock.json', + 'yarn-lock.json', + 'pnpm-lock.yaml' + ) + ).not.toThrow(); +} diff --git a/packages/nx/src/command-line/report.spec.ts b/packages/nx/src/command-line/report.spec.ts index a1d1c4e5df..c95baf5ea4 100644 --- a/packages/nx/src/command-line/report.spec.ts +++ b/packages/nx/src/command-line/report.spec.ts @@ -22,7 +22,6 @@ describe('report', () => { it('should read angular-devkit plugins', () => { jest.spyOn(fileUtils, 'readJsonFile').mockImplementation((path) => { - console.log(path); if (path === 'package.json') { return { dependencies: { @@ -32,6 +31,8 @@ describe('report', () => { 'plugin-two': '2.0.0', }, }; + } else if (path === 'nx.json') { + return {}; } }); jest.spyOn(packageJsonUtils, 'readModulePackageJson').mockImplementation( @@ -64,6 +65,8 @@ describe('report', () => { 'plugin-two': '2.0.0', }, }; + } else if (path === 'nx.json') { + return {}; } }); jest.spyOn(packageJsonUtils, 'readModulePackageJson').mockImplementation( @@ -91,6 +94,8 @@ describe('report', () => { 'plugin-two': '2.0.0', }, }; + } else if (path === 'nx.json') { + return {}; } }); jest.spyOn(packageJsonUtils, 'readModulePackageJson').mockImplementation( @@ -112,6 +117,47 @@ describe('report', () => { ]); }); + it('should read nx plugins from installations', () => { + jest.spyOn(fileUtils, 'readJsonFile').mockImplementation((path) => { + if (path === 'package.json') { + return { + dependencies: {}, + devDependencies: { + 'plugin-two': '2.0.0', + }, + }; + } else if (path === 'nx.json') { + return { + installation: { + version: '1.12.0', + plugins: { + 'plugin-one': '1.0.0', + }, + }, + }; + } + }); + jest.spyOn(packageJsonUtils, 'readModulePackageJson').mockImplementation( + provideMockPackages({ + 'plugin-one': { + 'nx-migrations': {}, + version: '1.0.0', + }, + 'plugin-two': { + generators: '', + version: '2.0.0', + }, + }) + ); + const plugins = findInstalledCommunityPlugins(); + expect(plugins).toEqual( + expect.arrayContaining([ + expect.objectContaining({ package: 'plugin-one', version: '1.0.0' }), + expect.objectContaining({ package: 'plugin-two', version: '2.0.0' }), + ]) + ); + }); + it('should not include non-plugins', () => { jest.spyOn(fileUtils, 'readJsonFile').mockImplementation((path) => { if (path === 'package.json') { @@ -124,6 +170,8 @@ describe('report', () => { 'other-package': '1.44.0', }, }; + } else if (path === 'nx.json') { + return {}; } }); jest.spyOn(packageJsonUtils, 'readModulePackageJson').mockImplementation( diff --git a/packages/nx/src/command-line/report.ts b/packages/nx/src/command-line/report.ts index 98844b6047..c2d8f5c1dc 100644 --- a/packages/nx/src/command-line/report.ts +++ b/packages/nx/src/command-line/report.ts @@ -19,6 +19,7 @@ import { readProjectsConfigurationFromProjectGraph, } from '../project-graph/project-graph'; import { gt, valid } from 'semver'; +import { NxJsonConfiguration } from '../config/nx-json'; const nxPackageJson = readJsonFile( join(__dirname, '../../package.json') @@ -190,7 +191,10 @@ async function findLocalPlugins() { function readPackageJson(p: string): PackageJson | null { try { - return readModulePackageJson(p).packageJson; + return readModulePackageJson(p, [ + workspaceRoot, + join(workspaceRoot, '.nx', 'installation'), + ]).packageJson; } catch { return null; } @@ -248,14 +252,9 @@ export function findMisalignedPackagesForPackage( export function findInstalledCommunityPlugins(): (PackageJson & { package: string; })[] { - const { dependencies, devDependencies } = readJsonFile( - join(workspaceRoot, 'package.json') - ); - const deps = [ - Object.keys(dependencies || {}), - Object.keys(devDependencies || {}), - ].flat(); - + const packageJsonDeps = getDependenciesFromPackageJson(); + const nxJsonDeps = getDependenciesFromNxJson(); + const deps = packageJsonDeps.concat(nxJsonDeps); return deps.reduce( (arr: any[], nextDep: string): { project: string; version: string }[] => { if ( @@ -302,3 +301,25 @@ export function findInstalledPackagesWeCareAbout() { return acc; }, [] as { package: string; version: string }[]); } + +function getDependenciesFromPackageJson( + packageJsonPath = 'package.json' +): string[] { + try { + const { dependencies, devDependencies } = readJsonFile( + join(workspaceRoot, packageJsonPath) + ); + return Object.keys({ ...dependencies, ...devDependencies }); + } catch {} + return []; +} + +function getDependenciesFromNxJson(): string[] { + const { installation } = readJsonFile( + join(workspaceRoot, 'nx.json') + ); + if (!installation) { + return []; + } + return ['nx', ...Object.keys(installation.plugins || {})]; +}