From 4dc69808f7a30ffe8bdb512730189e0d8ae57cb9 Mon Sep 17 00:00:00 2001 From: Jason Jean Date: Fri, 3 Nov 2023 16:58:16 -0400 Subject: [PATCH] chore(core): split package.json scripts from project.json plugin (#20041) --- .../package-json-next-to-project-json.spec.ts | 74 +++++++ .../package-json-next-to-project-json.ts | 58 ++++++ .../build-nodes/project-json.spec.ts | 195 +----------------- .../project-json/build-nodes/project-json.ts | 54 +---- packages/nx/src/utils/nx-plugin.ts | 11 +- 5 files changed, 139 insertions(+), 253 deletions(-) create mode 100644 packages/nx/src/plugins/project-json/build-nodes/package-json-next-to-project-json.spec.ts create mode 100644 packages/nx/src/plugins/project-json/build-nodes/package-json-next-to-project-json.ts 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 new file mode 100644 index 0000000000000..01f84e3cee47f --- /dev/null +++ b/packages/nx/src/plugins/project-json/build-nodes/package-json-next-to-project-json.spec.ts @@ -0,0 +1,74 @@ +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'; +const { createNodes } = CreatePackageJsonProjectsNextToProjectJson; + +describe('nx project.json plugin', () => { + let context: CreateNodesContext; + let createNodesFunction: CreateNodesFunction; + + beforeEach(() => { + context = { + nxJsonConfiguration: {}, + workspaceRoot: '/root', + }; + createNodesFunction = createNodes[1]; + }); + + it('should build projects from project.json', () => { + memfs.vol.fromJSON( + { + 'packages/lib-a/project.json': JSON.stringify({ + name: 'lib-a', + targets: { + build: { + executor: 'nx:run-commands', + options: {}, + }, + }, + }), + 'packages/lib-a/package.json': JSON.stringify({ + name: 'lib-a', + scripts: { + test: 'jest', + }, + }), + }, + '/root' + ); + + expect( + createNodesFunction('packages/lib-a/project.json', undefined, context) + ).toMatchInlineSnapshot(` + { + "projects": { + "lib-a": { + "name": "lib-a", + "root": "packages/lib-a", + "targets": { + "nx-release-publish": { + "dependsOn": [ + "^nx-release-publish", + ], + "executor": "@nx/js:release-publish", + "options": {}, + }, + "test": { + "executor": "nx:run-script", + "options": { + "script": "test", + }, + }, + }, + }, + }, + } + `); + }); +}); 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 new file mode 100644 index 0000000000000..5b8f5e57c2045 --- /dev/null +++ b/packages/nx/src/plugins/project-json/build-nodes/package-json-next-to-project-json.ts @@ -0,0 +1,58 @@ +import { dirname, join } from 'path'; +import { existsSync } from 'fs'; +import { NxPluginV2 } from '../../../utils/nx-plugin'; +import { readJsonFile } from '../../../utils/fileutils'; +import { ProjectConfiguration } from '../../../config/workspace-json-project-json'; +import { + PackageJson, + readTargetsFromPackageJson, +} from '../../../utils/package-json'; + +// TODO: Remove this one day, this should not need to be done. + +export const CreatePackageJsonProjectsNextToProjectJson: NxPluginV2 = { + name: 'nx-core-build-package-json-nodes-next-to-project-json-nodes', + createNodes: [ + '{project.json,**/project.json}', + (file, _, { workspaceRoot }) => { + const project = createProjectFromPackageJsonNextToProjectJson( + file, + workspaceRoot + ); + + return project + ? { + projects: { + [project.name]: project, + }, + } + : {}; + }, + ], +}; + +function createProjectFromPackageJsonNextToProjectJson( + projectJsonPath: string, + workspaceRoot: string +): ProjectConfiguration | null { + const root = dirname(projectJsonPath); + const packageJsonPath = join(workspaceRoot, root, 'package.json'); + if (!existsSync(packageJsonPath)) { + return null; + } + try { + const packageJson: PackageJson = readJsonFile(packageJsonPath); + + let { nx, name } = packageJson; + + return { + ...nx, + name, + root, + targets: readTargetsFromPackageJson(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 6ba798ca133ca..378f7e4558f7d 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 @@ -2,23 +2,10 @@ import * as memfs from 'memfs'; import '../../../internal-testing-utils/mock-fs'; -import { PackageJson } from '../../../utils/package-json'; - -import { - CreateProjectJsonProjectsPlugin, - mergeNpmScriptsWithTargets, -} from './project-json'; +import { CreateProjectJsonProjectsPlugin } from './project-json'; import { CreateNodesContext } from '../../../utils/nx-plugin'; const { createNodes } = CreateProjectJsonProjectsPlugin; -const defaultReleasePublishTarget = { - 'nx-release-publish': { - dependsOn: ['^nx-release-publish'], - executor: '@nx/js:release-publish', - options: {}, - }, -}; - describe('nx project.json plugin', () => { let context: CreateNodesContext; beforeEach(() => { @@ -44,13 +31,6 @@ describe('nx project.json plugin', () => { }, }, }), - 'packages/lib-a/package.json': JSON.stringify({ - name: 'lib-a', - scripts: { - build: 'should not see me', - test: 'jest', - }, - }), }, '/root' ); @@ -81,183 +61,10 @@ describe('nx project.json plugin', () => { "executor": "nx:run-commands", "options": {}, }, - "nx-release-publish": { - "dependsOn": [ - "^nx-release-publish", - ], - "executor": "@nx/js:release-publish", - "options": {}, - }, - "test": { - "executor": "nx:run-script", - "options": { - "script": "test", - }, - }, }, }, }, } `); }); - - describe('mergeNpmScriptsWithTargets', () => { - const packageJson: PackageJson = { - name: 'my-app', - version: '0.0.0', - scripts: { - build: 'echo 1', - }, - }; - - const packageJsonBuildTarget = { - executor: 'nx:run-script', - options: { - script: 'build', - }, - }; - - it('should prefer project.json targets', () => { - const projectJsonTargets = { - build: { - executor: 'nx:run-commands', - options: { - command: 'echo 2', - }, - }, - }; - - const result = mergeNpmScriptsWithTargets( - packageJson, - projectJsonTargets - ); - expect(result).toEqual({ - ...projectJsonTargets, - ...defaultReleasePublishTarget, - }); - }); - - it('should provide targets from project.json and package.json', () => { - const projectJsonTargets = { - clean: { - executor: 'nx:run-commands', - options: { - command: 'echo 2', - }, - }, - }; - - const result = mergeNpmScriptsWithTargets( - packageJson, - projectJsonTargets - ); - expect(result).toEqual({ - ...projectJsonTargets, - build: packageJsonBuildTarget, - ...defaultReleasePublishTarget, - }); - }); - - it('should contain extended options from nx property in package.json', () => { - const result = mergeNpmScriptsWithTargets( - { - name: 'my-other-app', - version: '', - scripts: { - build: 'echo 1', - }, - nx: { - targets: { - build: { - outputs: ['custom'], - }, - }, - }, - }, - null - ); - expect(result).toEqual({ - build: { ...packageJsonBuildTarget, outputs: ['custom'] }, - ...defaultReleasePublishTarget, - }); - }); - - it('should work when project.json targets is null', () => { - const result = mergeNpmScriptsWithTargets(packageJson, null); - - expect(result).toEqual({ - build: { - executor: 'nx:run-script', - options: { - script: 'build', - }, - }, - ...defaultReleasePublishTarget, - }); - }); - - it("should work when project root is ''", () => { - const result = mergeNpmScriptsWithTargets( - { - name: 'my-app', - version: '', - scripts: { - test: 'echo testing', - }, - }, - { - build: { - executor: 'nx:run-commands', - options: { command: 'echo hi' }, - }, - } - ); - - expect(result).toEqual({ - build: { - executor: 'nx:run-commands', - options: { command: 'echo hi' }, - }, - test: { - executor: 'nx:run-script', - options: { script: 'test' }, - }, - ...defaultReleasePublishTarget, - }); - }); - - it('should ignore scripts that are not in includedScripts', () => { - const result = mergeNpmScriptsWithTargets( - { - name: 'included-scripts-test', - version: '', - scripts: { - test: 'echo testing', - fail: 'exit 1', - }, - nx: { - includedScripts: ['test'], - }, - }, - { - build: { - executor: 'nx:run-commands', - options: { command: 'echo hi' }, - }, - } - ); - - expect(result).toEqual({ - build: { - executor: 'nx:run-commands', - options: { command: 'echo hi' }, - }, - test: { - executor: 'nx:run-script', - options: { script: 'test' }, - }, - ...defaultReleasePublishTarget, - }); - }); - }); }); 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 3d3596e3d01ec..513bb9380a8d3 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 @@ -1,17 +1,9 @@ import { dirname, join } from 'node:path'; -import { existsSync } from 'node:fs'; -import { - ProjectConfiguration, - TargetConfiguration, -} from '../../../config/workspace-json-project-json'; +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 { - PackageJson, - readTargetsFromPackageJson, -} from '../../../utils/package-json'; export const CreateProjectJsonProjectsPlugin: NxPluginV2 = { name: 'nx-core-build-project-json-nodes', @@ -21,7 +13,6 @@ export const CreateProjectJsonProjectsPlugin: NxPluginV2 = { const root = context.workspaceRoot; const json = readJsonFile(join(root, file)); const project = buildProjectFromProjectJson(json, file); - mergePackageJsonConfigurationWithProjectJson(project, root); return { projects: { [project.name]: project, @@ -41,46 +32,3 @@ export function buildProjectFromProjectJson( ...json, }; } - -export function mergePackageJsonConfigurationWithProjectJson( - p: ProjectConfiguration, - root: string -) { - if (existsSync(join(root, p.root, 'package.json'))) { - try { - const packageJson: PackageJson = readJsonFile( - join(root, p.root, 'package.json') - ); - - p.targets = mergeNpmScriptsWithTargets(packageJson, p.targets); - - const { nx } = packageJson; - - if (nx?.tags) { - p.tags = [...(p.tags || []), ...nx.tags]; - } - if (nx?.implicitDependencies) { - p.implicitDependencies = [ - ...(p.implicitDependencies || []), - ...nx.implicitDependencies, - ]; - } - if (nx?.namedInputs) { - p.namedInputs = { ...(p.namedInputs || {}), ...nx.namedInputs }; - } - } catch (e) { - // ignore json parser errors - } - } -} - -export function mergeNpmScriptsWithTargets( - packageJson: PackageJson, - targets: Record -): Record { - try { - return { ...readTargetsFromPackageJson(packageJson), ...(targets || {}) }; - } catch (e) { - return targets; - } -} diff --git a/packages/nx/src/utils/nx-plugin.ts b/packages/nx/src/utils/nx-plugin.ts index 94998916f6d71..6b00524979086 100644 --- a/packages/nx/src/utils/nx-plugin.ts +++ b/packages/nx/src/utils/nx-plugin.ts @@ -41,6 +41,7 @@ import { import { getNxPackageJsonWorkspacesPlugin } from '../../plugins/package-json-workspaces'; import { CreateProjectJsonProjectsPlugin } from '../plugins/project-json/build-nodes/project-json'; import { FileMapCache } from '../project-graph/nx-deps-cache'; +import { CreatePackageJsonProjectsNextToProjectJson } from '../plugins/project-json/build-nodes/package-json-next-to-project-json'; /** * Context for {@link CreateNodesFunction} @@ -286,11 +287,6 @@ export async function loadNxPlugins( ): Promise { const result: LoadedNxPlugin[] = [...(await getDefaultPlugins(root))]; - // TODO: These 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 - result.push(); - plugins ??= []; for (const plugin of plugins) { result.push(await loadNxPluginAsync(plugin, paths, root)); @@ -514,7 +510,10 @@ function readPluginMainFromProjectConfiguration( } async function getDefaultPlugins(root: string): Promise { - const plugins: NxPluginV2[] = [await import('../plugins/js')]; + const plugins: NxPluginV2[] = [ + CreatePackageJsonProjectsNextToProjectJson, + await import('../plugins/js'), + ]; if (shouldMergeAngularProjects(root, false)) { plugins.push(