From a1f69e3a01a4c442aa617707e2df3d2667bd1d06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leosvel=20P=C3=A9rez=20Espinosa?= Date: Thu, 19 Sep 2024 18:17:55 +0200 Subject: [PATCH] feat(graph): add sync generators to target details in project details view (#27639) Add a `Sync Generators` section to the target details in the PDV. ### Default ![image](https://github.com/user-attachments/assets/0390c301-a833-4230-8c6a-0d452a7b8c57) ### Heading tooltip ![image](https://github.com/user-attachments/assets/b8b1c19f-20f8-42e6-a914-16e4dd0b11f2) ### Source map ![image](https://github.com/user-attachments/assets/3b66d2e1-88b7-42e8-9771-5732a32b4cc3) ![image](https://github.com/user-attachments/assets/8b3b0f15-c2f1-411f-8eb6-1df6ff320c5c) ### Disabled sync generator tooltip ![image](https://github.com/user-attachments/assets/9fd35041-35fc-41c4-92b5-b613fed9e0ae) ## Current Behavior ## Expected Behavior ## Related Issue(s) Fixes # --- graph/client/src/app/routes.tsx | 2 + .../src/lib/project-details-page.tsx | 24 ++++--- .../src/lib/project-details-wrapper.tsx | 3 + .../project-details.stories.tsx | 6 ++ .../lib/project-details/project-details.tsx | 3 + ...arget-configuration-details-group-list.tsx | 5 ++ ...target-configuration-details-list-item.tsx | 3 + .../target-configuration-details.tsx | 68 +++++++++++++++++++ .../target-configuration-property-text.tsx | 31 +++++++++ .../target-configuration-property.tsx | 27 +++++++- .../src/lib/utils/sync-generators.ts | 34 ++++++++++ packages/nx/src/command-line/graph/graph.ts | 8 ++- 12 files changed, 202 insertions(+), 12 deletions(-) create mode 100644 graph/ui-project-details/src/lib/target-configuration-details/target-configuration-property-text.tsx create mode 100644 graph/ui-project-details/src/lib/utils/sync-generators.ts diff --git a/graph/client/src/app/routes.tsx b/graph/client/src/app/routes.tsx index 3a759244b33ec..d7426501041f0 100644 --- a/graph/client/src/app/routes.tsx +++ b/graph/client/src/app/routes.tsx @@ -84,6 +84,7 @@ const projectDetailsLoader = async ( sourceMap: Record; errors?: GraphError[]; connectedToCloud?: boolean; + disabledTaskSyncGenerators?: string[]; }> => { const workspaceData = await workspaceDataLoader(selectedWorkspaceId); const sourceMaps = await sourceMapsLoader(selectedWorkspaceId); @@ -104,6 +105,7 @@ const projectDetailsLoader = async ( sourceMap: sourceMaps[project.data.root], errors: workspaceData.errors, connectedToCloud: workspaceData.connectedToCloud, + disabledTaskSyncGenerators: workspaceData.disabledTaskSyncGenerators, }; }; diff --git a/graph/project-details/src/lib/project-details-page.tsx b/graph/project-details/src/lib/project-details-page.tsx index 73eb29b8d0ad0..b2f2bb7366ccc 100644 --- a/graph/project-details/src/lib/project-details-page.tsx +++ b/graph/project-details/src/lib/project-details-page.tsx @@ -21,14 +21,21 @@ import { import { ProjectDetailsHeader } from './project-details-header'; export function ProjectDetailsPage() { - const { project, sourceMap, hash, errors, connectedToCloud } = - useRouteLoaderData('selectedProjectDetails') as { - hash: string; - project: ProjectGraphProjectNode; - sourceMap: Record; - errors?: GraphError[]; - connectedToCloud?: boolean; - }; + const { + project, + sourceMap, + hash, + errors, + connectedToCloud, + disabledTaskSyncGenerators, + } = useRouteLoaderData('selectedProjectDetails') as { + hash: string; + project: ProjectGraphProjectNode; + sourceMap: Record; + errors?: GraphError[]; + connectedToCloud?: boolean; + disabledTaskSyncGenerators?: string[]; + }; const { environment, watch, appConfig } = useEnvironmentConfig(); @@ -65,6 +72,7 @@ export function ProjectDetailsPage() { sourceMap={sourceMap} errors={errors} connectedToCloud={connectedToCloud} + disabledTaskSyncGenerators={disabledTaskSyncGenerators} > diff --git a/graph/project-details/src/lib/project-details-wrapper.tsx b/graph/project-details/src/lib/project-details-wrapper.tsx index 9bc5911461a2b..03a253724bf16 100644 --- a/graph/project-details/src/lib/project-details-wrapper.tsx +++ b/graph/project-details/src/lib/project-details-wrapper.tsx @@ -22,6 +22,7 @@ interface ProjectDetailsProps { sourceMap: Record; errors?: GraphError[]; connectedToCloud?: boolean; + disabledTaskSyncGenerators?: string[]; } export function ProjectDetailsWrapper({ @@ -29,6 +30,7 @@ export function ProjectDetailsWrapper({ sourceMap, errors, connectedToCloud, + disabledTaskSyncGenerators, }: ProjectDetailsProps) { const environment = useEnvironmentConfig()?.environment; const externalApiService = getExternalApiService(); @@ -174,6 +176,7 @@ export function ProjectDetailsWrapper({ } connectedToCloud={connectedToCloud} onNxConnect={environment === 'nx-console' ? handleNxConnect : undefined} + disabledTaskSyncGenerators={disabledTaskSyncGenerators} /> diff --git a/graph/ui-project-details/src/lib/project-details/project-details.stories.tsx b/graph/ui-project-details/src/lib/project-details/project-details.stories.tsx index dfc131c1e087e..b8683ca9d67da 100644 --- a/graph/ui-project-details/src/lib/project-details/project-details.stories.tsx +++ b/graph/ui-project-details/src/lib/project-details/project-details.stories.tsx @@ -83,6 +83,11 @@ export const Primary = { ], }, configurations: {}, + syncGenerators: [ + '@nx/js:typescript-sync', + '@foo/bar:sync', + '@baz/qux:sync', + ], }, build: { dependsOn: ['build-base', 'build-native'], @@ -210,6 +215,7 @@ export const Primary = { 'nx-core-build-project-json-nodes', ], }, + disabledTaskSyncGenerators: ['@foo/bar:sync'], }, }; diff --git a/graph/ui-project-details/src/lib/project-details/project-details.tsx b/graph/ui-project-details/src/lib/project-details/project-details.tsx index 87e5165207841..77921e786afff 100644 --- a/graph/ui-project-details/src/lib/project-details/project-details.tsx +++ b/graph/ui-project-details/src/lib/project-details/project-details.tsx @@ -18,6 +18,7 @@ export interface ProjectDetailsProps { errors?: GraphError[]; variant?: 'default' | 'compact'; connectedToCloud?: boolean; + disabledTaskSyncGenerators?: string[]; onViewInProjectGraph?: (data: { projectName: string }) => void; onViewInTaskGraph?: (data: { projectName: string; @@ -44,6 +45,7 @@ export const ProjectDetails = ({ onNxConnect, viewInProjectGraphPosition = 'top', connectedToCloud, + disabledTaskSyncGenerators, }: ProjectDetailsProps) => { const projectData = project.data; const isCompact = variant === 'compact'; @@ -153,6 +155,7 @@ export const ProjectDetails = ({ onRunTarget={onRunTarget} onViewInTaskGraph={onViewInTaskGraph} connectedToCloud={connectedToCloud} + disabledTaskSyncGenerators={disabledTaskSyncGenerators} onNxConnect={onNxConnect} /> diff --git a/graph/ui-project-details/src/lib/target-configuration-details-group-list/target-configuration-details-group-list.tsx b/graph/ui-project-details/src/lib/target-configuration-details-group-list/target-configuration-details-group-list.tsx index 21f82053e1636..3580963fe1e95 100644 --- a/graph/ui-project-details/src/lib/target-configuration-details-group-list/target-configuration-details-group-list.tsx +++ b/graph/ui-project-details/src/lib/target-configuration-details-group-list/target-configuration-details-group-list.tsx @@ -18,6 +18,7 @@ export interface TargetConfigurationGroupListProps { }) => void; onNxConnect?: () => void; connectedToCloud?: boolean; + disabledTaskSyncGenerators?: string[]; className?: string; } @@ -30,6 +31,7 @@ export function TargetConfigurationGroupList({ onNxConnect, className = '', connectedToCloud, + disabledTaskSyncGenerators, }: TargetConfigurationGroupListProps) { const targetsGroup = useMemo(() => groupTargets(project), [project]); const hasGroups = useMemo(() => { @@ -56,6 +58,7 @@ export function TargetConfigurationGroupList({ project={project} sourceMap={sourceMap} connectedToCloud={connectedToCloud} + disabledTaskSyncGenerators={disabledTaskSyncGenerators} variant={variant} onRunTarget={onRunTarget} onViewInTaskGraph={onViewInTaskGraph} @@ -82,6 +85,7 @@ export function TargetConfigurationGroupList({ project={project} sourceMap={sourceMap} connectedToCloud={connectedToCloud} + disabledTaskSyncGenerators={disabledTaskSyncGenerators} variant={variant} onRunTarget={onRunTarget} onViewInTaskGraph={onViewInTaskGraph} @@ -105,6 +109,7 @@ export function TargetConfigurationGroupList({ project={project} sourceMap={sourceMap} connectedToCloud={connectedToCloud} + disabledTaskSyncGenerators={disabledTaskSyncGenerators} variant={variant} onRunTarget={onRunTarget} onViewInTaskGraph={onViewInTaskGraph} diff --git a/graph/ui-project-details/src/lib/target-configuration-details-list-item/target-configuration-details-list-item.tsx b/graph/ui-project-details/src/lib/target-configuration-details-list-item/target-configuration-details-list-item.tsx index 45fafea765876..2f3fc44dc4452 100644 --- a/graph/ui-project-details/src/lib/target-configuration-details-list-item/target-configuration-details-list-item.tsx +++ b/graph/ui-project-details/src/lib/target-configuration-details-list-item/target-configuration-details-list-item.tsx @@ -7,6 +7,7 @@ export interface TargetConfigurationDetailsListItemProps { project: ProjectGraphProjectNode; sourceMap: Record; connectedToCloud?: boolean; + disabledTaskSyncGenerators?: string[]; variant?: 'default' | 'compact'; onRunTarget?: (data: { projectName: string; targetName: string }) => void; onViewInTaskGraph?: (data: { @@ -23,6 +24,7 @@ export function TargetConfigurationDetailsListItem({ variant, sourceMap, connectedToCloud, + disabledTaskSyncGenerators, onRunTarget, onViewInTaskGraph, onNxConnect, @@ -42,6 +44,7 @@ export function TargetConfigurationDetailsListItem({ targetConfiguration={target} sourceMap={sourceMap} connectedToCloud={connectedToCloud} + disabledTaskSyncGenerators={disabledTaskSyncGenerators} onRunTarget={onRunTarget} onViewInTaskGraph={onViewInTaskGraph} onNxConnect={onNxConnect} diff --git a/graph/ui-project-details/src/lib/target-configuration-details/target-configuration-details.tsx b/graph/ui-project-details/src/lib/target-configuration-details/target-configuration-details.tsx index 0020c20c323b8..41d4a6bf135ca 100644 --- a/graph/ui-project-details/src/lib/target-configuration-details/target-configuration-details.tsx +++ b/graph/ui-project-details/src/lib/target-configuration-details/target-configuration-details.tsx @@ -14,6 +14,7 @@ import { TargetExecutorTitle } from '../target-executor/target-executor-title'; import { getTargetExecutorSourceMapKey } from '../target-source-info/get-target-executor-source-map-key'; import { TargetSourceInfo } from '../target-source-info/target-source-info'; import { getDisplayHeaderFromTargetConfiguration } from '../utils/get-display-header-from-target-configuration'; +import { getTaskSyncGenerators } from '../utils/sync-generators'; import { FadingCollapsible } from './fading-collapsible'; import { TargetConfigurationProperty } from './target-configuration-property'; import { TooltipTriggerText } from './tooltip-trigger-text'; @@ -24,6 +25,7 @@ interface TargetConfigurationDetailsProps { targetConfiguration: TargetConfiguration; sourceMap: Record; connectedToCloud?: boolean; + disabledTaskSyncGenerators?: string[]; variant?: 'default' | 'compact'; onCollapse?: (targetName: string) => void; onExpand?: (targetName: string) => void; @@ -43,6 +45,7 @@ export default function TargetConfigurationDetails({ targetConfiguration, sourceMap, connectedToCloud, + disabledTaskSyncGenerators, onViewInTaskGraph, onRunTarget, onNxConnect, @@ -84,6 +87,9 @@ export default function TargetConfigurationDetails({ ? Object.keys(configurations).length : true); + const { enabledSyncGenerators, disabledSyncGenerators } = + getTaskSyncGenerators(targetConfiguration, disabledTaskSyncGenerators); + return (
) : null} + + {enabledSyncGenerators.length > 0 && ( +
+

+ ) as any + } + > + + Sync Generators + + +

+
    + {enabledSyncGenerators.map((generator, idx) => ( +
  • + + + +
  • + ))} + {disabledSyncGenerators.length > 0 && + disabledSyncGenerators.map((generator, idx) => ( +
  • + + The Sync Generator is disabled in the{' '} + + sync.disabledTaskSyncGenerators + {' '} + property in the{' '} + nx.json{' '} + file. +

    + } + > + +
    +
  • + ))} +
+
+ )} )} diff --git a/graph/ui-project-details/src/lib/target-configuration-details/target-configuration-property-text.tsx b/graph/ui-project-details/src/lib/target-configuration-details/target-configuration-property-text.tsx new file mode 100644 index 0000000000000..68e30295ae5df --- /dev/null +++ b/graph/ui-project-details/src/lib/target-configuration-details/target-configuration-property-text.tsx @@ -0,0 +1,31 @@ +import { Tooltip } from '@nx/graph/ui-tooltips'; +import { JSX, ReactNode } from 'react'; +import { TooltipTriggerText } from './tooltip-trigger-text'; +import { QuestionMarkCircleIcon } from '@heroicons/react/24/outline'; + +interface TargetConfigurationPropertyTextProps { + content: ReactNode; + disabled?: boolean; + disabledTooltip?: ReactNode; +} + +export function TargetConfigurationPropertyText({ + content, + disabled, + disabledTooltip, +}: TargetConfigurationPropertyTextProps): JSX.Element | null { + return ( + <> + {content} + {disabledTooltip && ( + + + + + + + + )} + + ); +} diff --git a/graph/ui-project-details/src/lib/target-configuration-details/target-configuration-property.tsx b/graph/ui-project-details/src/lib/target-configuration-details/target-configuration-property.tsx index ce3b3e5ae5c4a..a2b02c8b81fc0 100644 --- a/graph/ui-project-details/src/lib/target-configuration-details/target-configuration-property.tsx +++ b/graph/ui-project-details/src/lib/target-configuration-details/target-configuration-property.tsx @@ -1,18 +1,27 @@ import { JSX, ReactNode } from 'react'; +import { TargetConfigurationPropertyText } from './target-configuration-property-text'; interface RenderPropertyProps { data: string | Record | any[]; + disabled?: boolean; + disabledTooltip?: ReactNode; children?: ReactNode; } export function TargetConfigurationProperty({ data, children, + disabled, + disabledTooltip, }: RenderPropertyProps): JSX.Element | null { if (typeof data === 'string') { return ( - {data} + {children} ); @@ -21,7 +30,11 @@ export function TargetConfigurationProperty({
    {data.map((item, index) => (
  • - {String(item)} + {children}
  • ))} @@ -32,7 +45,15 @@ export function TargetConfigurationProperty({
      {Object.entries(data).map(([key, value], index) => (
    • - {key}: {String(value)} + + {key}: {String(value)} + + } + disabled={disabled} + disabledTooltip={disabledTooltip} + /> {children}
    • ))} diff --git a/graph/ui-project-details/src/lib/utils/sync-generators.ts b/graph/ui-project-details/src/lib/utils/sync-generators.ts new file mode 100644 index 0000000000000..376513bbdda91 --- /dev/null +++ b/graph/ui-project-details/src/lib/utils/sync-generators.ts @@ -0,0 +1,34 @@ +/* eslint-disable @nx/enforce-module-boundaries */ +// nx-ignore-next-line +import type { TargetConfiguration } from '@nx/devkit'; + +export function getTaskSyncGenerators( + targetConfiguration: TargetConfiguration, + disabledTaskSyncGenerators: string[] | undefined +): { + enabledSyncGenerators: string[]; + disabledSyncGenerators: string[]; +} { + const enabledSyncGenerators: string[] = []; + const disabledSyncGenerators: string[] = []; + + if (!targetConfiguration.syncGenerators?.length) { + return { enabledSyncGenerators, disabledSyncGenerators }; + } + + if (!disabledTaskSyncGenerators?.length) { + enabledSyncGenerators.push(...targetConfiguration.syncGenerators); + return { enabledSyncGenerators, disabledSyncGenerators }; + } + + const disabledGeneratorsSet = new Set(disabledTaskSyncGenerators); + for (const generator of targetConfiguration.syncGenerators) { + if (disabledGeneratorsSet.has(generator)) { + disabledSyncGenerators.push(generator); + } else { + enabledSyncGenerators.push(generator); + } + } + + return { enabledSyncGenerators, disabledSyncGenerators }; +} diff --git a/packages/nx/src/command-line/graph/graph.ts b/packages/nx/src/command-line/graph/graph.ts index 583e62c89a370..90fdbeefb310e 100644 --- a/packages/nx/src/command-line/graph/graph.ts +++ b/packages/nx/src/command-line/graph/graph.ts @@ -78,6 +78,7 @@ export interface ProjectGraphClientResponse { isPartial: boolean; errors?: GraphError[]; connectedToCloud?: boolean; + disabledTaskSyncGenerators?: string[]; } export interface TaskGraphClientResponse { @@ -773,13 +774,16 @@ async function createProjectGraphAndSourceMapClientResponse( let isPartial = false; let errors: GraphError[] | undefined; let connectedToCloud: boolean | undefined; + let disabledTaskSyncGenerators: string[] | undefined; try { const projectGraphAndSourceMaps = await createProjectGraphAndSourceMapsAsync({ exitOnError: false }); projectGraph = projectGraphAndSourceMaps.projectGraph; sourceMaps = projectGraphAndSourceMaps.sourceMaps; - connectedToCloud = isNxCloudUsed(readNxJson()); + const nxJson = readNxJson(); + connectedToCloud = isNxCloudUsed(nxJson); + disabledTaskSyncGenerators = nxJson.sync?.disabledTaskSyncGenerators; } catch (e) { if (e instanceof ProjectGraphError) { projectGraph = e.getPartialProjectGraph(); @@ -820,6 +824,7 @@ async function createProjectGraphAndSourceMapClientResponse( sourceMaps, errors, connectedToCloud, + disabledTaskSyncGenerators, }) ); @@ -851,6 +856,7 @@ async function createProjectGraphAndSourceMapClientResponse( isPartial, errors, connectedToCloud, + disabledTaskSyncGenerators, }, sourceMapResponse: sourceMaps, };