feat(graph): add source info for command and script (#26162)
<!-- 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` --> ## Current Behavior <!-- This is the behavior we have today --> ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> <img width="993" alt="Screenshot 2024-05-28 at 12 19 15 PM" src="https://github.com/nrwl/nx/assets/16211801/35b95537-72ff-474f-b03a-68e20a7dfe55"> <img width="942" alt="Screenshot 2024-05-28 at 12 19 05 PM" src="https://github.com/nrwl/nx/assets/16211801/b67d920b-2689-452c-9214-d96ce12331dc"> <img width="728" alt="Screenshot 2024-05-28 at 12 09 15 PM" src="https://github.com/nrwl/nx/assets/16211801/c6e74976-83b5-44bf-b0b7-c99e22cd6e03"> ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #
This commit is contained in:
parent
7e984e11a6
commit
20529d4dc9
@ -4,10 +4,8 @@ import type { TargetConfiguration } from '@nx/devkit';
|
|||||||
|
|
||||||
import { JsonCodeBlock } from '@nx/graph/ui-code-block';
|
import { JsonCodeBlock } from '@nx/graph/ui-code-block';
|
||||||
import { useCallback, useContext, useEffect, useState } from 'react';
|
import { useCallback, useContext, useEffect, useState } from 'react';
|
||||||
import { SourceInfo } from '../source-info/source-info';
|
|
||||||
import { FadingCollapsible } from './fading-collapsible';
|
import { FadingCollapsible } from './fading-collapsible';
|
||||||
import { TargetConfigurationProperty } from './target-configuration-property';
|
import { TargetConfigurationProperty } from './target-configuration-property';
|
||||||
import { selectSourceInfo } from './target-configuration-details.util';
|
|
||||||
import { CopyToClipboard } from '../copy-to-clipboard/copy-to-clipboard';
|
import { CopyToClipboard } from '../copy-to-clipboard/copy-to-clipboard';
|
||||||
import { PropertyInfoTooltip, Tooltip } from '@nx/graph/ui-tooltips';
|
import { PropertyInfoTooltip, Tooltip } from '@nx/graph/ui-tooltips';
|
||||||
import { TooltipTriggerText } from './tooltip-trigger-text';
|
import { TooltipTriggerText } from './tooltip-trigger-text';
|
||||||
@ -17,6 +15,8 @@ import { ExpandedTargetsContext } from '@nx/graph/shared';
|
|||||||
import { getDisplayHeaderFromTargetConfiguration } from '../utils/get-display-header-from-target-configuration';
|
import { getDisplayHeaderFromTargetConfiguration } from '../utils/get-display-header-from-target-configuration';
|
||||||
import { TargetExecutor } from '../target-executor/target-executor';
|
import { TargetExecutor } from '../target-executor/target-executor';
|
||||||
import { TargetExecutorTitle } from '../target-executor/target-executor-title';
|
import { TargetExecutorTitle } from '../target-executor/target-executor-title';
|
||||||
|
import { TargetSourceInfo } from '../target-source-info/target-source-info';
|
||||||
|
import { getTargetExecutorSourceMapKey } from '../target-source-info/get-target-executor-source-map-key';
|
||||||
|
|
||||||
interface TargetConfigurationDetailsProps {
|
interface TargetConfigurationDetailsProps {
|
||||||
projectName: string;
|
projectName: string;
|
||||||
@ -109,7 +109,15 @@ export default function TargetConfigurationDetails({
|
|||||||
/>
|
/>
|
||||||
</h4>
|
</h4>
|
||||||
<p className="pl-5 font-mono">
|
<p className="pl-5 font-mono">
|
||||||
<TargetExecutor {...displayHeader} link={link} />
|
<TargetExecutor {...displayHeader} link={link}>
|
||||||
|
<TargetSourceInfo
|
||||||
|
className="pl-4 opacity-0 transition-opacity duration-150 ease-in-out group-hover/line:opacity-100"
|
||||||
|
propertyKey={`targets.${targetName}.${getTargetExecutorSourceMapKey(
|
||||||
|
targetConfiguration
|
||||||
|
)}`}
|
||||||
|
sourceMap={sourceMap}
|
||||||
|
/>
|
||||||
|
</TargetExecutor>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -122,7 +130,13 @@ export default function TargetConfigurationDetails({
|
|||||||
/>
|
/>
|
||||||
</h4>
|
</h4>
|
||||||
<p className="pl-5 font-mono">
|
<p className="pl-5 font-mono">
|
||||||
<TargetExecutor script={script} link={link} />
|
<TargetExecutor script={script} link={link}>
|
||||||
|
<TargetSourceInfo
|
||||||
|
className="pl-4 opacity-0 transition-opacity duration-150 ease-in-out group-hover/line:opacity-100"
|
||||||
|
propertyKey={`targets.${targetName}.options.script`}
|
||||||
|
sourceMap={sourceMap}
|
||||||
|
/>
|
||||||
|
</TargetExecutor>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -151,29 +165,20 @@ export default function TargetConfigurationDetails({
|
|||||||
</span>
|
</span>
|
||||||
</h4>
|
</h4>
|
||||||
<ul className="mb-4 list-disc pl-5">
|
<ul className="mb-4 list-disc pl-5">
|
||||||
{targetConfiguration.inputs.map((input, idx) => {
|
{targetConfiguration.inputs.map((input, idx) => (
|
||||||
const sourceInfo = selectSourceInfo(
|
|
||||||
sourceMap,
|
|
||||||
`targets.${targetName}.inputs`
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<li
|
<li
|
||||||
className="group/line overflow-hidden whitespace-nowrap"
|
className="group/line overflow-hidden whitespace-nowrap"
|
||||||
key={`input-${idx}`}
|
key={`input-${idx}`}
|
||||||
>
|
>
|
||||||
<TargetConfigurationProperty data={input}>
|
<TargetConfigurationProperty data={input}>
|
||||||
{sourceInfo && (
|
<TargetSourceInfo
|
||||||
<span className="inline flex min-w-0 pl-4 opacity-0 transition-opacity duration-150 ease-in-out group-hover/line:opacity-100">
|
className="min-w-0 flex-1 pl-4 opacity-0 transition-opacity duration-150 ease-in-out group-hover/line:opacity-100"
|
||||||
<SourceInfo
|
|
||||||
data={sourceInfo}
|
|
||||||
propertyKey={`targets.${targetName}.inputs`}
|
propertyKey={`targets.${targetName}.inputs`}
|
||||||
|
sourceMap={sourceMap}
|
||||||
/>
|
/>
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</TargetConfigurationProperty>
|
</TargetConfigurationProperty>
|
||||||
</li>
|
</li>
|
||||||
);
|
))}
|
||||||
})}
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -201,29 +206,20 @@ export default function TargetConfigurationDetails({
|
|||||||
</span>
|
</span>
|
||||||
</h4>
|
</h4>
|
||||||
<ul className="mb-4 list-disc pl-5">
|
<ul className="mb-4 list-disc pl-5">
|
||||||
{targetConfiguration.outputs?.map((output, idx) => {
|
{targetConfiguration.outputs?.map((output, idx) => (
|
||||||
const sourceInfo = selectSourceInfo(
|
|
||||||
sourceMap,
|
|
||||||
`targets.${targetName}.outputs`
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<li
|
<li
|
||||||
className="group/line overflow-hidden whitespace-nowrap"
|
className="group/line overflow-hidden whitespace-nowrap"
|
||||||
key={`output-${idx}`}
|
key={`output-${idx}`}
|
||||||
>
|
>
|
||||||
<TargetConfigurationProperty data={output}>
|
<TargetConfigurationProperty data={output}>
|
||||||
{sourceInfo && (
|
<TargetSourceInfo
|
||||||
<span className="inline flex min-w-0 pl-4 opacity-0 transition-opacity duration-150 ease-in-out group-hover/line:opacity-100">
|
className="min-w-0 flex-1 pl-4 opacity-0 transition-opacity duration-150 ease-in-out group-hover/line:opacity-100"
|
||||||
<SourceInfo
|
|
||||||
data={sourceInfo}
|
|
||||||
propertyKey={`targets.${targetName}.outputs`}
|
propertyKey={`targets.${targetName}.outputs`}
|
||||||
|
sourceMap={sourceMap}
|
||||||
/>
|
/>
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</TargetConfigurationProperty>
|
</TargetConfigurationProperty>
|
||||||
</li>
|
</li>
|
||||||
);
|
)) ?? <span>no outputs</span>}
|
||||||
}) ?? <span>no outputs</span>}
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -251,30 +247,20 @@ export default function TargetConfigurationDetails({
|
|||||||
</span>
|
</span>
|
||||||
</h4>
|
</h4>
|
||||||
<ul className="mb-4 list-disc pl-5">
|
<ul className="mb-4 list-disc pl-5">
|
||||||
{targetConfiguration.dependsOn.map((dep, idx) => {
|
{targetConfiguration.dependsOn.map((dep, idx) => (
|
||||||
const sourceInfo = selectSourceInfo(
|
|
||||||
sourceMap,
|
|
||||||
`targets.${targetName}.dependsOn`
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<li
|
<li
|
||||||
className="group/line overflow-hidden whitespace-nowrap"
|
className="group/line overflow-hidden whitespace-nowrap"
|
||||||
key={`dependsOn-${idx}`}
|
key={`dependsOn-${idx}`}
|
||||||
>
|
>
|
||||||
<TargetConfigurationProperty data={dep}>
|
<TargetConfigurationProperty data={dep}>
|
||||||
<span className="inline flex min-w-0 pl-4 opacity-0 transition-opacity duration-150 ease-in-out group-hover/line:opacity-100">
|
<TargetSourceInfo
|
||||||
{sourceInfo && (
|
className="min-w-0 flex-1 pl-4 opacity-0 transition-opacity duration-150 ease-in-out group-hover/line:opacity-100"
|
||||||
<SourceInfo
|
|
||||||
data={sourceInfo}
|
|
||||||
propertyKey={`targets.${targetName}.dependsOn`}
|
propertyKey={`targets.${targetName}.dependsOn`}
|
||||||
|
sourceMap={sourceMap}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</TargetConfigurationProperty>
|
</TargetConfigurationProperty>
|
||||||
</li>
|
</li>
|
||||||
);
|
))}
|
||||||
})}
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -295,20 +281,13 @@ export default function TargetConfigurationDetails({
|
|||||||
<FadingCollapsible>
|
<FadingCollapsible>
|
||||||
<JsonCodeBlock
|
<JsonCodeBlock
|
||||||
data={options}
|
data={options}
|
||||||
renderSource={(propertyName: string) => {
|
renderSource={(propertyName: string) => (
|
||||||
const sourceInfo = selectSourceInfo(
|
<TargetSourceInfo
|
||||||
sourceMap,
|
className="flex min-w-0 pl-4"
|
||||||
`targets.${targetName}.options.${propertyName}`
|
|
||||||
);
|
|
||||||
return sourceInfo ? (
|
|
||||||
<span className="flex min-w-0 pl-4">
|
|
||||||
<SourceInfo
|
|
||||||
data={sourceInfo}
|
|
||||||
propertyKey={`targets.${targetName}.options.${propertyName}`}
|
propertyKey={`targets.${targetName}.options.${propertyName}`}
|
||||||
|
sourceMap={sourceMap}
|
||||||
/>
|
/>
|
||||||
</span>
|
)}
|
||||||
) : null;
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</FadingCollapsible>
|
</FadingCollapsible>
|
||||||
</div>
|
</div>
|
||||||
@ -343,20 +322,13 @@ export default function TargetConfigurationDetails({
|
|||||||
<FadingCollapsible>
|
<FadingCollapsible>
|
||||||
<JsonCodeBlock
|
<JsonCodeBlock
|
||||||
data={targetConfiguration.configurations}
|
data={targetConfiguration.configurations}
|
||||||
renderSource={(propertyName: string) => {
|
renderSource={(propertyName: string) => (
|
||||||
const sourceInfo = selectSourceInfo(
|
<TargetSourceInfo
|
||||||
sourceMap,
|
className="flex min-w-0 pl-4"
|
||||||
`targets.${targetName}.configurations.${propertyName}`
|
|
||||||
);
|
|
||||||
return sourceInfo ? (
|
|
||||||
<span className="flex min-w-0 pl-4">
|
|
||||||
<SourceInfo
|
|
||||||
data={sourceInfo}
|
|
||||||
propertyKey={`targets.${targetName}.configurations.${propertyName}`}
|
propertyKey={`targets.${targetName}.configurations.${propertyName}`}
|
||||||
/>{' '}
|
sourceMap={sourceMap}
|
||||||
</span>
|
/>
|
||||||
) : null;
|
)}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</FadingCollapsible>
|
</FadingCollapsible>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -7,6 +7,7 @@ export interface TargetExecutorProps {
|
|||||||
executor?: string;
|
executor?: string;
|
||||||
isCompact?: boolean;
|
isCompact?: boolean;
|
||||||
link?: string;
|
link?: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TargetExecutor({
|
export function TargetExecutor({
|
||||||
@ -16,9 +17,16 @@ export function TargetExecutor({
|
|||||||
executor,
|
executor,
|
||||||
isCompact,
|
isCompact,
|
||||||
link,
|
link,
|
||||||
|
children,
|
||||||
}: TargetExecutorProps) {
|
}: TargetExecutorProps) {
|
||||||
if (script) {
|
if (script) {
|
||||||
return link ? <ExternalLink href={link}>{script}</ExternalLink> : script;
|
return link ? (
|
||||||
|
<div className="group/line">
|
||||||
|
<ExternalLink href={link}>{script}</ExternalLink> {children}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
script
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (commands) {
|
if (commands) {
|
||||||
@ -34,19 +42,22 @@ export function TargetExecutor({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
<div className="group/line">
|
||||||
<ul>
|
<ul>
|
||||||
{commands?.map((c) =>
|
{commands?.filter(Boolean).map((c) => (
|
||||||
c ? (
|
|
||||||
<li>{link ? <ExternalLink href={link}>{c}</ExternalLink> : c}</li>
|
<li>{link ? <ExternalLink href={link}>{c}</ExternalLink> : c}</li>
|
||||||
) : null
|
))}
|
||||||
)}
|
|
||||||
</ul>
|
</ul>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayText = command ?? executor ?? '';
|
const displayText = command ?? executor ?? '';
|
||||||
return link ? (
|
return link ? (
|
||||||
<ExternalLink href={link}>{displayText}</ExternalLink>
|
<div className="group/line">
|
||||||
|
<ExternalLink href={link}>{displayText}</ExternalLink> {children}
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
displayText
|
displayText
|
||||||
);
|
);
|
||||||
|
|||||||
@ -0,0 +1,17 @@
|
|||||||
|
/* eslint-disable @nx/enforce-module-boundaries */
|
||||||
|
// nx-ignore-next-line
|
||||||
|
import type { TargetConfiguration } from '@nx/devkit';
|
||||||
|
|
||||||
|
export function getTargetExecutorSourceMapKey(
|
||||||
|
targetConfiguration: TargetConfiguration
|
||||||
|
): string {
|
||||||
|
if (targetConfiguration.options?.command) {
|
||||||
|
return 'options.command';
|
||||||
|
} else if (targetConfiguration.options?.commands) {
|
||||||
|
return 'options.commands';
|
||||||
|
} else if (targetConfiguration.options?.script) {
|
||||||
|
return 'options.script';
|
||||||
|
} else {
|
||||||
|
return 'executor';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { selectSourceInfo } from './target-configuration-details.util';
|
import { selectSourceInfo } from './select-source-info';
|
||||||
|
|
||||||
test('selectSourceInfo', () => {
|
test('selectSourceInfo', () => {
|
||||||
const map = {
|
const map = {
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
/* eslint-disable @nx/enforce-module-boundaries */
|
||||||
|
// nx-ignore-next-line
|
||||||
|
import { SourceInfo } from '../source-info/source-info';
|
||||||
|
import { selectSourceInfo } from './select-source-info';
|
||||||
|
|
||||||
|
export interface TargetSourceInfoProps {
|
||||||
|
propertyKey: string;
|
||||||
|
sourceMap: Record<string, string[]>;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TargetSourceInfo({
|
||||||
|
propertyKey,
|
||||||
|
sourceMap,
|
||||||
|
className,
|
||||||
|
}: TargetSourceInfoProps) {
|
||||||
|
const sourceInfo = selectSourceInfo(sourceMap, propertyKey);
|
||||||
|
if (!sourceInfo) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<span className={className}>
|
||||||
|
<SourceInfo data={sourceInfo} propertyKey={propertyKey} />
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,19 +1,25 @@
|
|||||||
import { ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline';
|
import { ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline';
|
||||||
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
export function ExternalLink({
|
export function ExternalLink({
|
||||||
children,
|
children,
|
||||||
href,
|
href,
|
||||||
title,
|
title,
|
||||||
|
className,
|
||||||
}: {
|
}: {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
href: string;
|
href: string;
|
||||||
|
className?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
href={href}
|
href={href}
|
||||||
title={title}
|
title={title}
|
||||||
className="gap-2 text-slate-500 hover:underline dark:text-slate-400"
|
className={twMerge(
|
||||||
|
'gap-2 text-slate-500 hover:underline dark:text-slate-400',
|
||||||
|
className
|
||||||
|
)}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user