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  ### Heading tooltip  ### Source map   ### Disabled sync generator tooltip  <!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> <!-- Fixes NXC-802 --> Fixes #
This commit is contained in:
parent
7d0d834e42
commit
a1f69e3a01
@ -84,6 +84,7 @@ const projectDetailsLoader = async (
|
||||
sourceMap: Record<string, string[]>;
|
||||
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,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -21,13 +21,20 @@ import {
|
||||
import { ProjectDetailsHeader } from './project-details-header';
|
||||
|
||||
export function ProjectDetailsPage() {
|
||||
const { project, sourceMap, hash, errors, connectedToCloud } =
|
||||
useRouteLoaderData('selectedProjectDetails') as {
|
||||
const {
|
||||
project,
|
||||
sourceMap,
|
||||
hash,
|
||||
errors,
|
||||
connectedToCloud,
|
||||
disabledTaskSyncGenerators,
|
||||
} = useRouteLoaderData('selectedProjectDetails') as {
|
||||
hash: string;
|
||||
project: ProjectGraphProjectNode;
|
||||
sourceMap: Record<string, string[]>;
|
||||
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}
|
||||
></ProjectDetailsWrapper>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -22,6 +22,7 @@ interface ProjectDetailsProps {
|
||||
sourceMap: Record<string, string[]>;
|
||||
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}
|
||||
/>
|
||||
<ErrorToast errors={errors} />
|
||||
</>
|
||||
|
||||
@ -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'],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -7,6 +7,7 @@ export interface TargetConfigurationDetailsListItemProps {
|
||||
project: ProjectGraphProjectNode;
|
||||
sourceMap: Record<string, string[]>;
|
||||
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}
|
||||
|
||||
@ -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<string, string[]>;
|
||||
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 (
|
||||
<div className="relative rounded-md border border-slate-200 dark:border-slate-700/60">
|
||||
<TargetConfigurationDetailsHeader
|
||||
@ -374,6 +380,68 @@ export default function TargetConfigurationDetails({
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{enabledSyncGenerators.length > 0 && (
|
||||
<div className="group">
|
||||
<h4 className="mb-4">
|
||||
<Tooltip
|
||||
openAction="hover"
|
||||
content={
|
||||
(<PropertyInfoTooltip type="syncGenerators" />) as any
|
||||
}
|
||||
>
|
||||
<span className="font-medium">
|
||||
<TooltipTriggerText>Sync Generators</TooltipTriggerText>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</h4>
|
||||
<ul className="mb-4 list-disc pl-5">
|
||||
{enabledSyncGenerators.map((generator, idx) => (
|
||||
<li
|
||||
className="group/line overflow-hidden whitespace-nowrap"
|
||||
key={`syncGenerators-${idx}`}
|
||||
>
|
||||
<TargetConfigurationProperty data={generator}>
|
||||
<TargetSourceInfo
|
||||
className="min-w-0 flex-1 pl-4 opacity-0 transition-opacity duration-150 ease-in-out group-hover/line:opacity-100"
|
||||
propertyKey={`targets.${targetName}.syncGenerators`}
|
||||
sourceMap={sourceMap}
|
||||
/>
|
||||
</TargetConfigurationProperty>
|
||||
</li>
|
||||
))}
|
||||
{disabledSyncGenerators.length > 0 &&
|
||||
disabledSyncGenerators.map((generator, idx) => (
|
||||
<li
|
||||
className="group/line overflow-hidden whitespace-nowrap"
|
||||
key={`syncGenerators-${idx}`}
|
||||
>
|
||||
<TargetConfigurationProperty
|
||||
data={generator}
|
||||
disabled={true}
|
||||
disabledTooltip={
|
||||
<p className="max-w-sm whitespace-pre-wrap py-2 font-mono text-sm normal-case text-slate-700 dark:text-slate-400">
|
||||
The Sync Generator is disabled in the{' '}
|
||||
<code className="font-bold italic">
|
||||
sync.disabledTaskSyncGenerators
|
||||
</code>{' '}
|
||||
property in the{' '}
|
||||
<code className="font-bold italic">nx.json</code>{' '}
|
||||
file.
|
||||
</p>
|
||||
}
|
||||
>
|
||||
<TargetSourceInfo
|
||||
className="min-w-0 flex-1 pl-4 opacity-0 transition-opacity duration-150 ease-in-out group-hover/line:opacity-100"
|
||||
propertyKey={`targets.${targetName}.syncGenerators`}
|
||||
sourceMap={sourceMap}
|
||||
/>
|
||||
</TargetConfigurationProperty>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -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 (
|
||||
<>
|
||||
<span className={disabled ? 'opacity-50' : ''}>{content}</span>
|
||||
{disabledTooltip && (
|
||||
<Tooltip openAction="hover" content={disabledTooltip}>
|
||||
<span className="pl-2 font-medium">
|
||||
<TooltipTriggerText>
|
||||
<QuestionMarkCircleIcon className="inline h-4 w-4" />
|
||||
</TooltipTriggerText>
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -1,18 +1,27 @@
|
||||
import { JSX, ReactNode } from 'react';
|
||||
import { TargetConfigurationPropertyText } from './target-configuration-property-text';
|
||||
|
||||
interface RenderPropertyProps {
|
||||
data: string | Record<string, any> | any[];
|
||||
disabled?: boolean;
|
||||
disabledTooltip?: ReactNode;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export function TargetConfigurationProperty({
|
||||
data,
|
||||
children,
|
||||
disabled,
|
||||
disabledTooltip,
|
||||
}: RenderPropertyProps): JSX.Element | null {
|
||||
if (typeof data === 'string') {
|
||||
return (
|
||||
<span className="flex font-mono text-sm">
|
||||
{data}
|
||||
<TargetConfigurationPropertyText
|
||||
content={data}
|
||||
disabled={disabled}
|
||||
disabledTooltip={disabledTooltip}
|
||||
/>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
@ -21,7 +30,11 @@ export function TargetConfigurationProperty({
|
||||
<ul>
|
||||
{data.map((item, index) => (
|
||||
<li key={index} className="flex font-mono text-sm">
|
||||
{String(item)}
|
||||
<TargetConfigurationPropertyText
|
||||
content={String(item)}
|
||||
disabled={disabled}
|
||||
disabledTooltip={disabledTooltip}
|
||||
/>
|
||||
{children}
|
||||
</li>
|
||||
))}
|
||||
@ -32,7 +45,15 @@ export function TargetConfigurationProperty({
|
||||
<ul>
|
||||
{Object.entries(data).map(([key, value], index) => (
|
||||
<li key={index} className="flex font-mono text-sm">
|
||||
<TargetConfigurationPropertyText
|
||||
content={
|
||||
<>
|
||||
<strong>{key}</strong>: {String(value)}
|
||||
</>
|
||||
}
|
||||
disabled={disabled}
|
||||
disabledTooltip={disabledTooltip}
|
||||
/>
|
||||
{children}
|
||||
</li>
|
||||
))}
|
||||
|
||||
34
graph/ui-project-details/src/lib/utils/sync-generators.ts
Normal file
34
graph/ui-project-details/src/lib/utils/sync-generators.ts
Normal file
@ -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 };
|
||||
}
|
||||
@ -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,
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user