From 51f5fe4434225540e2e20aa66ed7566749050ba8 Mon Sep 17 00:00:00 2001 From: Jason Jean Date: Tue, 16 Jul 2024 12:46:57 -0500 Subject: [PATCH] =?UTF-8?q?fix(core):=20merge=20package.json=20plugins=20a?= =?UTF-8?q?nd=20updated=20project.json=20plugin=E2=80=A6=20(#26952)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … using v2 ## Current Behavior The `package.json` handling is split between before and after plugins so it's confusing. Also, `package.json` nodes will get overwritten by plugins while others won't. ## Expected Behavior The `package.json` handling is merged. Also, the `package.json` nodes will always overwrite plugins as intended. ## Related Issue(s) Fixes # --- packages/jest/src/plugins/plugin.ts | 2 +- packages/nx/plugins/package-json.ts | 2 +- .../generators/utils/project-configuration.ts | 2 +- .../plugins/package-json-workspaces/index.ts | 2 - .../create-nodes.spec.ts | 446 ++++++++++-------- .../create-nodes.ts | 71 ++- packages/nx/src/plugins/package-json/index.ts | 2 + .../package-json-next-to-project-json.spec.ts | 126 ----- .../package-json-next-to-project-json.ts | 75 --- .../build-nodes/project-json.spec.ts | 64 +-- .../project-json/build-nodes/project-json.ts | 32 +- packages/nx/src/project-graph/file-utils.ts | 2 +- .../src/project-graph/plugins/internal-api.ts | 8 +- .../nx/src/project-graph/plugins/loader.ts | 4 +- .../src/project-graph/plugins/utils.spec.ts | 14 + .../nx/src/project-graph/plugins/utils.ts | 4 +- packages/nx/src/utils/nx-plugin.deprecated.ts | 2 +- 17 files changed, 393 insertions(+), 465 deletions(-) delete mode 100644 packages/nx/src/plugins/package-json-workspaces/index.ts rename packages/nx/src/plugins/{package-json-workspaces => package-json}/create-nodes.spec.ts (63%) rename packages/nx/src/plugins/{package-json-workspaces => package-json}/create-nodes.ts (79%) create mode 100644 packages/nx/src/plugins/package-json/index.ts delete mode 100644 packages/nx/src/plugins/project-json/build-nodes/package-json-next-to-project-json.spec.ts delete mode 100644 packages/nx/src/plugins/project-json/build-nodes/package-json-next-to-project-json.ts diff --git a/packages/jest/src/plugins/plugin.ts b/packages/jest/src/plugins/plugin.ts index 6c86cbb151dfa..e82eb2114de25 100644 --- a/packages/jest/src/plugins/plugin.ts +++ b/packages/jest/src/plugins/plugin.ts @@ -25,7 +25,7 @@ import { clearRequireCache, loadConfigFile, } from '@nx/devkit/src/utils/config-utils'; -import { getGlobPatternsFromPackageManagerWorkspaces } from 'nx/src/plugins/package-json-workspaces'; +import { getGlobPatternsFromPackageManagerWorkspaces } from 'nx/src/plugins/package-json'; import { combineGlobPatterns } from 'nx/src/utils/globs'; import { minimatch } from 'minimatch'; import { hashObject } from 'nx/src/devkit-internals'; diff --git a/packages/nx/plugins/package-json.ts b/packages/nx/plugins/package-json.ts index 936ca3e8970a4..502e0da250204 100644 --- a/packages/nx/plugins/package-json.ts +++ b/packages/nx/plugins/package-json.ts @@ -1,6 +1,6 @@ import type { NxPluginV2 } from '../src/project-graph/plugins'; import { workspaceRoot } from '../src/utils/workspace-root'; -import { createNodeFromPackageJson } from '../src/plugins/package-json-workspaces'; +import { createNodeFromPackageJson } from '../src/plugins/package-json'; const plugin: NxPluginV2 = { name: 'nx-all-package-jsons-plugin', diff --git a/packages/nx/src/generators/utils/project-configuration.ts b/packages/nx/src/generators/utils/project-configuration.ts index a698e26cc5d04..3158ee93db28a 100644 --- a/packages/nx/src/generators/utils/project-configuration.ts +++ b/packages/nx/src/generators/utils/project-configuration.ts @@ -4,7 +4,7 @@ import { basename, join, relative } from 'path'; import { buildProjectConfigurationFromPackageJson, getGlobPatternsFromPackageManagerWorkspaces, -} from '../../plugins/package-json-workspaces'; +} from '../../plugins/package-json'; import { buildProjectFromProjectJson } from '../../plugins/project-json/build-nodes/project-json'; import { renamePropertyWithStableKeys } from '../../adapter/angular-json'; import { diff --git a/packages/nx/src/plugins/package-json-workspaces/index.ts b/packages/nx/src/plugins/package-json-workspaces/index.ts deleted file mode 100644 index 7ac34ae661e87..0000000000000 --- a/packages/nx/src/plugins/package-json-workspaces/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './create-nodes'; -export const name = 'nx/core/package-json-workspaces'; diff --git a/packages/nx/src/plugins/package-json-workspaces/create-nodes.spec.ts b/packages/nx/src/plugins/package-json/create-nodes.spec.ts similarity index 63% rename from packages/nx/src/plugins/package-json-workspaces/create-nodes.spec.ts rename to packages/nx/src/plugins/package-json/create-nodes.spec.ts index 3bb8afcae0717..7560f89db2f13 100644 --- a/packages/nx/src/plugins/package-json-workspaces/create-nodes.spec.ts +++ b/packages/nx/src/plugins/package-json/create-nodes.spec.ts @@ -1,7 +1,7 @@ import '../../internal-testing-utils/mock-fs'; import { vol } from 'memfs'; -import { createNodeFromPackageJson, createNodes } from './create-nodes'; +import { createNodeFromPackageJson, createNodesV2 } from './create-nodes'; describe('nx package.json workspaces plugin', () => { const context = { @@ -198,7 +198,7 @@ describe('nx package.json workspaces plugin', () => { }); describe('negative patterns', () => { - it('should work based on negative patterns defined in package.json workspaces', () => { + it('should work based on negative patterns defined in package.json workspaces', async () => { vol.fromJSON( { 'package.json': JSON.stringify({ @@ -231,64 +231,74 @@ describe('nx package.json workspaces plugin', () => { // No matching project based on the package.json "workspace" config expect( - createNodes[1]('package.json', undefined, context) - ).toMatchInlineSnapshot(`{}`); + await createNodesV2[1](['package.json'], undefined, context) + ).toMatchInlineSnapshot(`[]`); // Matching project based on the package.json "workspace" config - expect(createNodes[1]('packages/vite/package.json', undefined, context)) - .toMatchInlineSnapshot(` - { - "projects": { - "packages/vite": { - "metadata": { - "description": undefined, - "targetGroups": {}, - }, - "name": "vite", - "root": "packages/vite", - "sourceRoot": "packages/vite", - "tags": [ - "npm:public", - ], - "targets": { - "nx-release-publish": { - "dependsOn": [ - "^nx-release-publish", + expect( + await createNodesV2[1]( + ['packages/vite/package.json'], + undefined, + context + ) + ).toMatchInlineSnapshot(` + [ + [ + "packages/vite/package.json", + { + "projects": { + "packages/vite": { + "metadata": { + "description": undefined, + "targetGroups": {}, + }, + "name": "vite", + "root": "packages/vite", + "sourceRoot": "packages/vite", + "tags": [ + "npm:public", ], - "executor": "@nx/js:release-publish", - "options": {}, + "targets": { + "nx-release-publish": { + "dependsOn": [ + "^nx-release-publish", + ], + "executor": "@nx/js:release-publish", + "options": {}, + }, + }, }, }, }, - }, - } + ], + ] `); // No matching project based on the package.json "workspace" config expect( - createNodes[1]('packages/fs/package.json', undefined, context) - ).toMatchInlineSnapshot(`{}`); + await createNodesV2[1](['packages/fs/package.json'], undefined, context) + ).toMatchInlineSnapshot(`[]`); // No matching project based on the package.json "workspace" config expect( - createNodes[1]( - 'packages/orm-browser-example/package.json', + await createNodesV2[1]( + ['packages/orm-browser-example/package.json'], undefined, context ) - ).toMatchInlineSnapshot(`{}`); + ).toMatchInlineSnapshot(`[]`); // No matching project based on the package.json "workspace" config expect( - createNodes[1]( - 'packages/framework-examples/package.json', + await createNodesV2[1]( + ['packages/framework-examples/package.json'], undefined, context ) - ).toMatchInlineSnapshot(`{}`); + ).toMatchInlineSnapshot(`[]`); }); - it('should work based on negative patterns defined in pnpm-workspace.yaml', () => { + it('should work based on negative patterns defined in pnpm-workspace.yaml', async () => { vol.fromJSON( { 'package.json': JSON.stringify({ name: 'root' }), @@ -319,64 +329,74 @@ describe('nx package.json workspaces plugin', () => { // No matching project based on the pnpm-workspace.yaml "packages" config expect( - createNodes[1]('package.json', undefined, context) - ).toMatchInlineSnapshot(`{}`); + await createNodesV2[1](['package.json'], undefined, context) + ).toMatchInlineSnapshot(`[]`); // Matching project based on the pnpm-workspace.yaml "packages" config - expect(createNodes[1]('packages/vite/package.json', undefined, context)) - .toMatchInlineSnapshot(` - { - "projects": { - "packages/vite": { - "metadata": { - "description": undefined, - "targetGroups": {}, - }, - "name": "vite", - "root": "packages/vite", - "sourceRoot": "packages/vite", - "tags": [ - "npm:public", - ], - "targets": { - "nx-release-publish": { - "dependsOn": [ - "^nx-release-publish", + expect( + await createNodesV2[1]( + ['packages/vite/package.json'], + undefined, + context + ) + ).toMatchInlineSnapshot(` + [ + [ + "packages/vite/package.json", + { + "projects": { + "packages/vite": { + "metadata": { + "description": undefined, + "targetGroups": {}, + }, + "name": "vite", + "root": "packages/vite", + "sourceRoot": "packages/vite", + "tags": [ + "npm:public", ], - "executor": "@nx/js:release-publish", - "options": {}, + "targets": { + "nx-release-publish": { + "dependsOn": [ + "^nx-release-publish", + ], + "executor": "@nx/js:release-publish", + "options": {}, + }, + }, }, }, }, - }, - } + ], + ] `); // No matching project based on the pnpm-workspace.yaml "packages" config expect( - createNodes[1]('packages/fs/package.json', undefined, context) - ).toMatchInlineSnapshot(`{}`); + await createNodesV2[1](['packages/fs/package.json'], undefined, context) + ).toMatchInlineSnapshot(`[]`); // No matching project based on the pnpm-workspace.yaml "packages" config expect( - createNodes[1]( - 'packages/orm-browser-example/package.json', + await createNodesV2[1]( + ['packages/orm-browser-example/package.json'], undefined, context ) - ).toMatchInlineSnapshot(`{}`); + ).toMatchInlineSnapshot(`[]`); // No matching project based on the pnpm-workspace.yaml "packages" config expect( - createNodes[1]( - 'packages/framework-examples/package.json', + await createNodesV2[1]( + ['packages/framework-examples/package.json'], undefined, context ) - ).toMatchInlineSnapshot(`{}`); + ).toMatchInlineSnapshot(`[]`); }); - it('should work based on negative patterns defined in lerna.json', () => { + it('should work based on negative patterns defined in lerna.json', async () => { vol.fromJSON( { 'package.json': JSON.stringify({ name: 'root' }), @@ -403,66 +423,76 @@ describe('nx package.json workspaces plugin', () => { // No matching project based on the lerna.json "packages" config expect( - createNodes[1]('package.json', undefined, context) - ).toMatchInlineSnapshot(`{}`); + await createNodesV2[1](['package.json'], undefined, context) + ).toMatchInlineSnapshot(`[]`); // Matching project based on the lerna.json "packages" config - expect(createNodes[1]('packages/vite/package.json', undefined, context)) - .toMatchInlineSnapshot(` - { - "projects": { - "packages/vite": { - "metadata": { - "description": undefined, - "targetGroups": {}, - }, - "name": "vite", - "root": "packages/vite", - "sourceRoot": "packages/vite", - "tags": [ - "npm:public", - ], - "targets": { - "nx-release-publish": { - "dependsOn": [ - "^nx-release-publish", + expect( + await createNodesV2[1]( + ['packages/vite/package.json'], + undefined, + context + ) + ).toMatchInlineSnapshot(` + [ + [ + "packages/vite/package.json", + { + "projects": { + "packages/vite": { + "metadata": { + "description": undefined, + "targetGroups": {}, + }, + "name": "vite", + "root": "packages/vite", + "sourceRoot": "packages/vite", + "tags": [ + "npm:public", ], - "executor": "@nx/js:release-publish", - "options": {}, + "targets": { + "nx-release-publish": { + "dependsOn": [ + "^nx-release-publish", + ], + "executor": "@nx/js:release-publish", + "options": {}, + }, + }, }, }, }, - }, - } + ], + ] `); // No matching project based on the lerna.json "packages" config expect( - createNodes[1]('packages/fs/package.json', undefined, context) - ).toMatchInlineSnapshot(`{}`); + await createNodesV2[1](['packages/fs/package.json'], undefined, context) + ).toMatchInlineSnapshot(`[]`); // No matching project based on the lerna.json "packages" config expect( - createNodes[1]( - 'packages/orm-browser-example/package.json', + await createNodesV2[1]( + ['packages/orm-browser-example/package.json'], undefined, context ) - ).toMatchInlineSnapshot(`{}`); + ).toMatchInlineSnapshot(`[]`); // No matching project based on the lerna.json "packages" config expect( - createNodes[1]( - 'packages/framework-examples/package.json', + await createNodesV2[1]( + ['packages/framework-examples/package.json'], undefined, context ) - ).toMatchInlineSnapshot(`{}`); + ).toMatchInlineSnapshot(`[]`); }); }); describe('sibling project.json files', () => { - it('should add a script target if the sibling project.json file does not exist', () => { + it('should add a script target if the sibling project.json file does not exist', async () => { vol.fromJSON( { 'package.json': JSON.stringify({ @@ -479,51 +509,57 @@ describe('nx package.json workspaces plugin', () => { '/root' ); - expect(createNodes[1]('packages/a/package.json', undefined, context)) - .toMatchInlineSnapshot(` - { - "projects": { - "packages/a": { - "metadata": { - "description": undefined, - "targetGroups": { - "NPM Scripts": [ - "build", - ], - }, - }, - "name": "root", - "root": "packages/a", - "sourceRoot": "packages/a", - "tags": [ - "npm:public", - ], - "targets": { - "build": { - "executor": "nx:run-script", + expect( + await createNodesV2[1](['packages/a/package.json'], undefined, context) + ).toMatchInlineSnapshot(` + [ + [ + "packages/a/package.json", + { + "projects": { + "packages/a": { "metadata": { - "runCommand": "npm run build", - "scriptContent": "echo build", + "description": undefined, + "targetGroups": { + "NPM Scripts": [ + "build", + ], + }, }, - "options": { - "script": "build", - }, - }, - "nx-release-publish": { - "dependsOn": [ - "^nx-release-publish", + "name": "root", + "root": "packages/a", + "sourceRoot": "packages/a", + "tags": [ + "npm:public", ], - "executor": "@nx/js:release-publish", - "options": {}, + "targets": { + "build": { + "executor": "nx:run-script", + "metadata": { + "runCommand": "npm run build", + "scriptContent": "echo build", + }, + "options": { + "script": "build", + }, + }, + "nx-release-publish": { + "dependsOn": [ + "^nx-release-publish", + ], + "executor": "@nx/js:release-publish", + "options": {}, + }, + }, }, }, }, - }, - } + ], + ] `); }); - it('should add a script target if the sibling project.json exists but does not have a conflicting target', () => { + it('should add a script target if the sibling project.json exists but does not have a conflicting target', async () => { vol.fromJSON( { 'package.json': JSON.stringify({ @@ -547,51 +583,57 @@ describe('nx package.json workspaces plugin', () => { '/root' ); - expect(createNodes[1]('packages/a/package.json', undefined, context)) - .toMatchInlineSnapshot(` - { - "projects": { - "packages/a": { - "metadata": { - "description": undefined, - "targetGroups": { - "NPM Scripts": [ - "build", - ], - }, - }, - "name": "root", - "root": "packages/a", - "sourceRoot": "packages/a", - "tags": [ - "npm:public", - ], - "targets": { - "build": { - "executor": "nx:run-script", + expect( + await createNodesV2[1](['packages/a/package.json'], undefined, context) + ).toMatchInlineSnapshot(` + [ + [ + "packages/a/package.json", + { + "projects": { + "packages/a": { "metadata": { - "runCommand": "npm run build", - "scriptContent": "echo build", + "description": undefined, + "targetGroups": { + "NPM Scripts": [ + "build", + ], + }, }, - "options": { - "script": "build", - }, - }, - "nx-release-publish": { - "dependsOn": [ - "^nx-release-publish", + "name": "root", + "root": "packages/a", + "sourceRoot": "packages/a", + "tags": [ + "npm:public", ], - "executor": "@nx/js:release-publish", - "options": {}, + "targets": { + "build": { + "executor": "nx:run-script", + "metadata": { + "runCommand": "npm run build", + "scriptContent": "echo build", + }, + "options": { + "script": "build", + }, + }, + "nx-release-publish": { + "dependsOn": [ + "^nx-release-publish", + ], + "executor": "@nx/js:release-publish", + "options": {}, + }, + }, }, }, }, - }, - } + ], + ] `); }); - it('should not add a script target if the sibling project.json exists and has a conflicting target', () => { + it('should not add a script target if the sibling project.json exists and has a conflicting target', async () => { vol.fromJSON( { 'package.json': JSON.stringify({ @@ -615,33 +657,39 @@ describe('nx package.json workspaces plugin', () => { '/root' ); - expect(createNodes[1]('packages/a/package.json', undefined, context)) - .toMatchInlineSnapshot(` - { - "projects": { - "packages/a": { - "metadata": { - "description": undefined, - "targetGroups": {}, - }, - "name": "root", - "root": "packages/a", - "sourceRoot": "packages/a", - "tags": [ - "npm:public", - ], - "targets": { - "nx-release-publish": { - "dependsOn": [ - "^nx-release-publish", + expect( + await createNodesV2[1](['packages/a/package.json'], undefined, context) + ).toMatchInlineSnapshot(` + [ + [ + "packages/a/package.json", + { + "projects": { + "packages/a": { + "metadata": { + "description": undefined, + "targetGroups": {}, + }, + "name": "root", + "root": "packages/a", + "sourceRoot": "packages/a", + "tags": [ + "npm:public", ], - "executor": "@nx/js:release-publish", - "options": {}, + "targets": { + "nx-release-publish": { + "dependsOn": [ + "^nx-release-publish", + ], + "executor": "@nx/js:release-publish", + "options": {}, + }, + }, }, }, }, - }, - } + ], + ] `); }); }); diff --git a/packages/nx/src/plugins/package-json-workspaces/create-nodes.ts b/packages/nx/src/plugins/package-json/create-nodes.ts similarity index 79% rename from packages/nx/src/plugins/package-json-workspaces/create-nodes.ts rename to packages/nx/src/plugins/package-json/create-nodes.ts index e3d0e8963e0d3..d722ae90554c9 100644 --- a/packages/nx/src/plugins/package-json-workspaces/create-nodes.ts +++ b/packages/nx/src/plugins/package-json/create-nodes.ts @@ -16,22 +16,71 @@ import { readTargetsFromPackageJson, } from '../../utils/package-json'; import { joinPathFragments } from '../../utils/path'; -import { CreateNodes } from '../../project-graph/plugins'; +import { + createNodesFromFiles, + CreateNodesV2, +} from '../../project-graph/plugins'; +import { basename } from 'path'; -export const createNodes: CreateNodes = [ - combineGlobPatterns('package.json', '**/package.json'), - (p, _, { workspaceRoot }) => { - const readJson = (f) => readJsonFile(join(workspaceRoot, f)); - const matcher = buildPackageJsonWorkspacesMatcher(workspaceRoot, readJson); +export const createNodesV2: CreateNodesV2 = [ + combineGlobPatterns( + 'package.json', + '**/package.json', + 'project.json', + '**/project.json' + ), + (configFiles, _, context) => { + const { packageJsons, projectJsonRoots } = splitConfigFiles(configFiles); - if (matcher(p)) { - return createNodeFromPackageJson(p, workspaceRoot); - } - // The given package.json is not part of the workspaces configuration. - return {}; + const readJson = (f) => readJsonFile(join(context.workspaceRoot, f)); + const isInPackageJsonWorkspaces = buildPackageJsonWorkspacesMatcher( + context.workspaceRoot, + readJson + ); + const isNextToProjectJson = (packageJsonPath: string) => { + return projectJsonRoots.has(dirname(packageJsonPath)); + }; + + return createNodesFromFiles( + (packageJsonPath, options, context) => { + if ( + !isInPackageJsonWorkspaces(packageJsonPath) && + !isNextToProjectJson(packageJsonPath) + ) { + // Skip if package.json is not part of the package.json workspaces and not next to a project.json. + return null; + } + + return createNodeFromPackageJson( + packageJsonPath, + context.workspaceRoot + ); + }, + packageJsons, + _, + context + ); }, ]; +function splitConfigFiles(configFiles: readonly string[]): { + packageJsons: string[]; + projectJsonRoots: Set; +} { + const packageJsons = []; + const projectJsonRoots = new Set(); + + for (const configFile of configFiles) { + if (basename(configFile) === 'package.json') { + packageJsons.push(configFile); + } else { + projectJsonRoots.add(dirname(configFile)); + } + } + + return { packageJsons, projectJsonRoots }; +} + export function buildPackageJsonWorkspacesMatcher( workspaceRoot: string, readJson: (string) => any diff --git a/packages/nx/src/plugins/package-json/index.ts b/packages/nx/src/plugins/package-json/index.ts new file mode 100644 index 0000000000000..67457b6c04edb --- /dev/null +++ b/packages/nx/src/plugins/package-json/index.ts @@ -0,0 +1,2 @@ +export * from './create-nodes'; +export const name = 'nx/core/package-json'; 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 deleted file mode 100644 index 6d7607eca9d37..0000000000000 --- a/packages/nx/src/plugins/project-json/build-nodes/package-json-next-to-project-json.spec.ts +++ /dev/null @@ -1,126 +0,0 @@ -import * as memfs from 'memfs'; - -import '../../../internal-testing-utils/mock-fs'; - -import { PackageJsonProjectsNextToProjectJsonPlugin } from './package-json-next-to-project-json'; -import { CreateNodesContext } from '../../../project-graph/plugins'; -const { createNodes } = PackageJsonProjectsNextToProjectJsonPlugin; - -describe('nx project.json plugin', () => { - let context: CreateNodesContext; - let createNodesFunction = createNodes[1]; - - beforeEach(() => { - context = { - nxJsonConfiguration: {}, - workspaceRoot: '/root', - configFiles: [], - }; - }); - - it('should build projects from package.json next to project.json', () => { - memfs.vol.fromJSON( - { - 'package.json': JSON.stringify({ - name: 'lib-a', - description: 'lib-a project description', - }), - 'packages/lib-a/project.json': JSON.stringify({ - name: 'lib-a', - description: 'lib-a project description', - targets: { - build: { - executor: 'nx:run-commands', - options: {}, - }, - }, - }), - 'packages/lib-a/package.json': JSON.stringify({ - name: 'lib-a', - description: 'lib-a package description', - scripts: { - test: 'jest', - }, - }), - }, - '/root' - ); - - expect( - createNodesFunction('packages/lib-a/project.json', undefined, context) - ).toMatchInlineSnapshot(` - { - "projects": { - "lib-a": { - "metadata": { - "description": "lib-a package description", - "targetGroups": { - "NPM Scripts": [ - "test", - ], - }, - }, - "name": "lib-a", - "root": "packages/lib-a", - "tags": [ - "npm:public", - ], - "targets": { - "nx-release-publish": { - "dependsOn": [ - "^nx-release-publish", - ], - "executor": "@nx/js:release-publish", - "options": {}, - }, - "test": { - "executor": "nx:run-script", - "metadata": { - "runCommand": "npm run test", - "scriptContent": "jest", - }, - "options": { - "script": "test", - }, - }, - }, - }, - }, - } - `); - }); - - it('should not build package manager workspace projects from package.json next to project.json', () => { - memfs.vol.fromJSON( - { - 'package.json': JSON.stringify({ - name: 'lib-a', - description: 'lib-a project description', - workspaces: ['packages/lib-a'], - }), - 'packages/lib-a/project.json': JSON.stringify({ - name: 'lib-a', - description: 'lib-a project description', - targets: { - build: { - executor: 'nx:run-commands', - options: {}, - }, - }, - }), - 'packages/lib-a/package.json': JSON.stringify({ - name: 'lib-a', - description: 'lib-a package description', - scripts: { - test: 'jest', - }, - }), - }, - '/root' - ); - - expect( - createNodesFunction('packages/lib-a/project.json', undefined, context) - ).toMatchInlineSnapshot(`{}`); - }); -}); diff --git a/packages/nx/src/plugins/project-json/build-nodes/package-json-next-to-project-json.ts b/packages/nx/src/plugins/project-json/build-nodes/package-json-next-to-project-json.ts deleted file mode 100644 index bfdb6f5ca372c..0000000000000 --- a/packages/nx/src/plugins/project-json/build-nodes/package-json-next-to-project-json.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { dirname, join } from 'path'; -import { existsSync } from 'fs'; -import { NxPluginV2 } from '../../../project-graph/plugins'; -import { readJsonFile } from '../../../utils/fileutils'; -import { ProjectConfiguration } from '../../../config/workspace-json-project-json'; -import { - PackageJson, - getMetadataFromPackageJson, - getTagsFromPackageJson, - readTargetsFromPackageJson, -} from '../../../utils/package-json'; -import { buildPackageJsonWorkspacesMatcher } from '../../package-json-workspaces'; - -// TODO: Remove this one day, this should not need to be done. - -export const PackageJsonProjectsNextToProjectJsonPlugin: NxPluginV2 = { - // Its not a problem if plugins happen to have same name, and this - // will look least confusing in the source map. - name: 'nx/core/package-json', - createNodes: [ - '{project.json,**/project.json}', - (file, _, { workspaceRoot }) => { - const project = createProjectFromPackageJsonNextToProjectJson( - file, - workspaceRoot - ); - - return project - ? { - projects: { - [project.name]: project, - }, - } - : {}; - }, - ], -}; - -export default PackageJsonProjectsNextToProjectJsonPlugin; - -function createProjectFromPackageJsonNextToProjectJson( - projectJsonPath: string, - workspaceRoot: string -): ProjectConfiguration | null { - const root = dirname(projectJsonPath); - const relativePackageJsonPath = join(root, 'package.json'); - const packageJsonPath = join(workspaceRoot, relativePackageJsonPath); - const readJson = (f) => readJsonFile(join(workspaceRoot, f)); - - // Do not create projects for package.json files - // that are part of the package manager workspaces - // Those package.json files will be processed later on - const matcher = buildPackageJsonWorkspacesMatcher(workspaceRoot, readJson); - - if (!existsSync(packageJsonPath) || matcher(relativePackageJsonPath)) { - return null; - } - try { - const packageJson: PackageJson = readJsonFile(packageJsonPath); - - let { nx, name } = packageJson; - - return { - ...nx, - name, - root, - targets: readTargetsFromPackageJson(packageJson), - metadata: getMetadataFromPackageJson(packageJson), - tags: getTagsFromPackageJson(packageJson), - } as ProjectConfiguration; - } catch (e) { - console.log(e); - return null; - } -} diff --git a/packages/nx/src/plugins/project-json/build-nodes/project-json.spec.ts b/packages/nx/src/plugins/project-json/build-nodes/project-json.spec.ts index 9a2141cc660f7..0a516783f1a29 100644 --- a/packages/nx/src/plugins/project-json/build-nodes/project-json.spec.ts +++ b/packages/nx/src/plugins/project-json/build-nodes/project-json.spec.ts @@ -4,7 +4,7 @@ import '../../../internal-testing-utils/mock-fs'; import { ProjectJsonProjectsPlugin } from './project-json'; import { CreateNodesContext } from '../../../project-graph/plugins'; -const { createNodes } = ProjectJsonProjectsPlugin; +const { createNodesV2 } = ProjectJsonProjectsPlugin; describe('nx project.json plugin', () => { let context: CreateNodesContext; @@ -16,7 +16,7 @@ describe('nx project.json plugin', () => { }; }); - it('should build projects from project.json', () => { + it('should build projects from project.json', async () => { memfs.vol.fromJSON( { 'project.json': JSON.stringify({ @@ -36,36 +36,46 @@ describe('nx project.json plugin', () => { '/root' ); - expect(createNodes[1]('project.json', undefined, context)) - .toMatchInlineSnapshot(` - { - "projects": { - ".": { - "name": "root", - "root": ".", - "targets": { - "command": "echo root project", + expect( + await createNodesV2[1]( + ['project.json', 'packages/lib-a/project.json'], + undefined, + context + ) + ).toMatchInlineSnapshot(` + [ + [ + "project.json", + { + "projects": { + ".": { + "name": "root", + "root": ".", + "targets": { + "command": "echo root project", + }, + }, }, }, - }, - } - `); - expect(createNodes[1]('packages/lib-a/project.json', undefined, context)) - .toMatchInlineSnapshot(` - { - "projects": { - "packages/lib-a": { - "name": "lib-a", - "root": "packages/lib-a", - "targets": { - "build": { - "executor": "nx:run-commands", - "options": {}, + ], + [ + "packages/lib-a/project.json", + { + "projects": { + "packages/lib-a": { + "name": "lib-a", + "root": "packages/lib-a", + "targets": { + "build": { + "executor": "nx:run-commands", + "options": {}, + }, + }, }, }, }, - }, - } + ], + ] `); }); }); 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 e99530e2416b6..43512d064b40e 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,25 +3,35 @@ import { dirname, join } from 'node:path'; import { ProjectConfiguration } from '../../../config/workspace-json-project-json'; import { toProjectName } from '../../../config/to-project-name'; import { readJsonFile } from '../../../utils/fileutils'; -import { NxPluginV2 } from '../../../project-graph/plugins'; +import { + createNodesFromFiles, + NxPluginV2, +} from '../../../project-graph/plugins'; import { PackageJson } from '../../../utils/package-json'; export const ProjectJsonProjectsPlugin: NxPluginV2 = { name: 'nx/core/project-json', - createNodes: [ + createNodesV2: [ '{project.json,**/project.json}', - (file, _, { workspaceRoot }) => { - const json = readJsonFile( - join(workspaceRoot, file) - ); + (configFiles, _, context) => { + return createNodesFromFiles( + (file) => { + const json = readJsonFile( + join(context.workspaceRoot, file) + ); - const project = buildProjectFromProjectJson(json, file); + const project = buildProjectFromProjectJson(json, file); - return { - projects: { - [project.root]: project, + return { + projects: { + [project.root]: project, + }, + }; }, - }; + configFiles, + _, + context + ); }, ], }; diff --git a/packages/nx/src/project-graph/file-utils.ts b/packages/nx/src/project-graph/file-utils.ts index aa5c0fb81336e..d0db3a6f5767f 100644 --- a/packages/nx/src/project-graph/file-utils.ts +++ b/packages/nx/src/project-graph/file-utils.ts @@ -24,7 +24,7 @@ import { import { buildProjectConfigurationFromPackageJson, getGlobPatternsFromPackageManagerWorkspaces, -} from '../plugins/package-json-workspaces'; +} from '../plugins/package-json'; import { globWithWorkspaceContextSync } from '../utils/workspace-context'; import { buildProjectFromProjectJson } from '../plugins/project-json/build-nodes/project-json'; import { PackageJson } from '../utils/package-json'; diff --git a/packages/nx/src/project-graph/plugins/internal-api.ts b/packages/nx/src/project-graph/plugins/internal-api.ts index ce98a61a9fd8a..460d260511ef4 100644 --- a/packages/nx/src/project-graph/plugins/internal-api.ts +++ b/packages/nx/src/project-graph/plugins/internal-api.ts @@ -192,12 +192,6 @@ async function normalizePlugins(plugins: PluginConfiguration[], root: string) { plugins ??= []; return [ - // This plugin adds targets that we want to be able to overwrite - // in any user-land plugin, so it has to be first :). - join( - __dirname, - '../../plugins/project-json/build-nodes/package-json-next-to-project-json' - ), ...plugins, // Most of the nx core node plugins go on the end, s.t. it overwrites any other plugins ...(await getDefaultPlugins(root)), @@ -210,7 +204,7 @@ export async function getDefaultPlugins(root: string) { ...(shouldMergeAngularProjects(root, false) ? [join(__dirname, '../../adapter/angular-json')] : []), - join(__dirname, '../../plugins/package-json-workspaces'), + join(__dirname, '../../plugins/package-json'), join(__dirname, '../../plugins/project-json/build-nodes/project-json'), ]; } diff --git a/packages/nx/src/project-graph/plugins/loader.ts b/packages/nx/src/project-graph/plugins/loader.ts index 9a57eee95a910..7dd5b85c3a5ce 100644 --- a/packages/nx/src/project-graph/plugins/loader.ts +++ b/packages/nx/src/project-graph/plugins/loader.ts @@ -293,7 +293,9 @@ async function importPluginModule(pluginPath: string): Promise { const m = await import(pluginPath); if ( m.default && - ('createNodes' in m.default || 'createDependencies' in m.default) + ('createNodes' in m.default || + 'createNodesV2' in m.default || + 'createDependencies' in m.default) ) { return m.default; } diff --git a/packages/nx/src/project-graph/plugins/utils.spec.ts b/packages/nx/src/project-graph/plugins/utils.spec.ts index e2d81ee797278..1b09d42049621 100644 --- a/packages/nx/src/project-graph/plugins/utils.spec.ts +++ b/packages/nx/src/project-graph/plugins/utils.spec.ts @@ -183,4 +183,18 @@ describe('createNodesFromFiles', () => { `); } }); + + it('should filter out null results', async () => { + const createNodes = [ + '**/*', + () => { + return null; + }, + ] as const; + const options = {}; + + expect( + await createNodesFromFiles(createNodes[1], configFiles, options, context) + ).toEqual([]); + }); }); diff --git a/packages/nx/src/project-graph/plugins/utils.ts b/packages/nx/src/project-graph/plugins/utils.ts index 8ad35be4e7131..2ca250f2f282b 100644 --- a/packages/nx/src/project-graph/plugins/utils.ts +++ b/packages/nx/src/project-graph/plugins/utils.ts @@ -73,7 +73,9 @@ export async function createNodesFromFiles( ...context, configFiles, }); - results.push([file, value] as const); + if (value) { + results.push([file, value] as const); + } } catch (e) { errors.push([file, e] as const); } diff --git a/packages/nx/src/utils/nx-plugin.deprecated.ts b/packages/nx/src/utils/nx-plugin.deprecated.ts index 22f3644794995..fab1f31942ad7 100644 --- a/packages/nx/src/utils/nx-plugin.deprecated.ts +++ b/packages/nx/src/utils/nx-plugin.deprecated.ts @@ -2,7 +2,7 @@ import { shouldMergeAngularProjects } from '../adapter/angular-json'; import { ProjectGraphProcessor } from '../config/project-graph'; import { TargetConfiguration } from '../config/workspace-json-project-json'; import ProjectJsonProjectsPlugin from '../plugins/project-json/build-nodes/project-json'; -import * as PackageJsonWorkspacesPlugin from '../plugins/package-json-workspaces'; +import * as PackageJsonWorkspacesPlugin from '../plugins/package-json'; import { NxPluginV2 } from '../project-graph/plugins'; /**