diff --git a/graph/client/src/app/external-api-impl.ts b/graph/client/src/app/external-api-impl.ts index 724c9ebf87817..ba2b6d08cf698 100644 --- a/graph/client/src/app/external-api-impl.ts +++ b/graph/client/src/app/external-api-impl.ts @@ -25,40 +25,10 @@ export class ExternalApiImpl extends ExternalApi { console.log('graphInteractionEventListener not registered.'); return; } - if (type === 'file-click') { - const url = `${payload.sourceRoot}/${payload.file}`; - this.graphInteractionEventListener({ - type: 'file-click', - payload: { url }, - }); - } else if (type === 'open-project-config') { - this.graphInteractionEventListener({ - type: 'open-project-config', - payload, - }); - } else if (type === 'run-task') { - this.graphInteractionEventListener({ - type: 'run-task', - payload, - }); - } else if (type === 'open-project-graph') { - this.graphInteractionEventListener({ - type: 'open-project-graph', - payload, - }); - } else if (type === 'open-task-graph') { - this.graphInteractionEventListener({ - type: 'open-task-graph', - payload, - }); - } else if (type === 'override-target') { - this.graphInteractionEventListener({ - type: 'override-target', - payload, - }); - } else { - console.log('unhandled event', type, payload); - } + this.graphInteractionEventListener({ + type, + payload, + }); } ); diff --git a/graph/client/src/app/routes.tsx b/graph/client/src/app/routes.tsx index 9b247bba98d91..a724f8fdba151 100644 --- a/graph/client/src/app/routes.tsx +++ b/graph/client/src/app/routes.tsx @@ -83,6 +83,7 @@ const projectDetailsLoader = async ( project: ProjectGraphProjectNode; sourceMap: Record; errors?: GraphError[]; + connectedToCloud?: boolean; }> => { const workspaceData = await workspaceDataLoader(selectedWorkspaceId); const sourceMaps = await sourceMapsLoader(selectedWorkspaceId); @@ -102,6 +103,7 @@ const projectDetailsLoader = async ( project, sourceMap: sourceMaps[project.data.root], errors: workspaceData.errors, + connectedToCloud: workspaceData.connectedToCloud, }; }; diff --git a/graph/client/src/app/ui-tooltips/graph-tooltip-display.tsx b/graph/client/src/app/ui-tooltips/graph-tooltip-display.tsx index 77408c63cec04..2e7281884a8ad 100644 --- a/graph/client/src/app/ui-tooltips/graph-tooltip-display.tsx +++ b/graph/client/src/app/ui-tooltips/graph-tooltip-display.tsx @@ -69,8 +69,7 @@ export function TooltipDisplay() { externalApiService.postEvent({ type: 'file-click', payload: { - sourceRoot: currentTooltip.props.sourceRoot, - file: url, + url: `${currentTooltip.props.sourceRoot}/${url}`, }, }) : undefined; diff --git a/graph/project-details/src/lib/project-details-page.tsx b/graph/project-details/src/lib/project-details-page.tsx index 9bcb7b7d41365..73eb29b8d0ad0 100644 --- a/graph/project-details/src/lib/project-details-page.tsx +++ b/graph/project-details/src/lib/project-details-page.tsx @@ -21,14 +21,14 @@ import { import { ProjectDetailsHeader } from './project-details-header'; export function ProjectDetailsPage() { - const { project, sourceMap, hash, errors } = useRouteLoaderData( - 'selectedProjectDetails' - ) as { - hash: string; - project: ProjectGraphProjectNode; - sourceMap: Record; - errors?: GraphError[]; - }; + const { project, sourceMap, hash, errors, connectedToCloud } = + useRouteLoaderData('selectedProjectDetails') as { + hash: string; + project: ProjectGraphProjectNode; + sourceMap: Record; + errors?: GraphError[]; + connectedToCloud?: boolean; + }; const { environment, watch, appConfig } = useEnvironmentConfig(); @@ -64,6 +64,7 @@ export function ProjectDetailsPage() { project={project} sourceMap={sourceMap} errors={errors} + connectedToCloud={connectedToCloud} > diff --git a/graph/project-details/src/lib/project-details-wrapper.tsx b/graph/project-details/src/lib/project-details-wrapper.tsx index 3682765914438..6ee8cd4364d99 100644 --- a/graph/project-details/src/lib/project-details-wrapper.tsx +++ b/graph/project-details/src/lib/project-details-wrapper.tsx @@ -21,12 +21,14 @@ interface ProjectDetailsProps { project: ProjectGraphProjectNode; sourceMap: Record; errors?: GraphError[]; + connectedToCloud?: boolean; } export function ProjectDetailsWrapper({ project, sourceMap, errors, + connectedToCloud, }: ProjectDetailsProps) { const environment = useEnvironmentConfig()?.environment; const externalApiService = getExternalApiService(); @@ -95,6 +97,14 @@ export function ProjectDetailsWrapper({ [externalApiService] ); + const handleNxConnect = useCallback( + () => + externalApiService.postEvent({ + type: 'nx-connect', + }), + [externalApiService] + ); + const updateSearchParams = ( params: URLSearchParams, targetNames?: string[] @@ -162,6 +172,10 @@ export function ProjectDetailsWrapper({ viewInProjectGraphPosition={ environment === 'nx-console' ? 'bottom' : 'top' } + connectedToCloud={connectedToCloud} + nxConnectCallback={ + environment === 'nx-console' ? handleNxConnect : undefined + } /> diff --git a/graph/shared/src/lib/external-api-service.ts b/graph/shared/src/lib/external-api-service.ts index 0ad21108f1484..1b9426dc5b03e 100644 --- a/graph/shared/src/lib/external-api-service.ts +++ b/graph/shared/src/lib/external-api-service.ts @@ -9,10 +9,10 @@ export function getExternalApiService() { } export class ExternalApiService { - private subscribers: Set<(event: { type: string; payload: any }) => void> = + private subscribers: Set<(event: { type: string; payload?: any }) => void> = new Set(); - postEvent(event: { type: string; payload: any }) { + postEvent(event: { type: string; payload?: any }) { this.subscribers.forEach((subscriber) => { subscriber(event); }); diff --git a/graph/ui-icons/src/index.ts b/graph/ui-icons/src/index.ts index 829dd6931b316..cc3c66aa312c9 100644 --- a/graph/ui-icons/src/index.ts +++ b/graph/ui-icons/src/index.ts @@ -1,2 +1,3 @@ export * from './lib/technology-icon'; export * from './lib/framework-icons'; +export * from './lib/ nx-cloud-icon'; diff --git a/graph/ui-icons/src/lib/ nx-cloud-icon.tsx b/graph/ui-icons/src/lib/ nx-cloud-icon.tsx new file mode 100644 index 0000000000000..805e0fb0cff41 --- /dev/null +++ b/graph/ui-icons/src/lib/ nx-cloud-icon.tsx @@ -0,0 +1,17 @@ +import { FC, SVGProps } from 'react'; + +export const NxCloudIcon: FC> = (props) => ( + + + +); 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 dd2b4cc532d8c..b51838f44cdb4 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 @@ -19,12 +19,14 @@ export interface ProjectDetailsProps { sourceMap: Record; errors?: GraphError[]; variant?: 'default' | 'compact'; + connectedToCloud?: boolean; onViewInProjectGraph?: (data: { projectName: string }) => void; onViewInTaskGraph?: (data: { projectName: string; targetName: string; }) => void; onRunTarget?: (data: { projectName: string; targetName: string }) => void; + nxConnectCallback?: () => void; viewInProjectGraphPosition?: 'top' | 'bottom'; } @@ -41,7 +43,9 @@ export const ProjectDetails = ({ onViewInProjectGraph, onViewInTaskGraph, onRunTarget, + nxConnectCallback, viewInProjectGraphPosition = 'top', + connectedToCloud, }: ProjectDetailsProps) => { const projectData = project.data; const isCompact = variant === 'compact'; @@ -161,6 +165,8 @@ export const ProjectDetails = ({ variant={variant} onRunTarget={onRunTarget} onViewInTaskGraph={onViewInTaskGraph} + connectedToCloud={connectedToCloud} + nxConnectCallback={nxConnectCallback} /> diff --git a/graph/ui-project-details/src/lib/target-configuration-details-group-container/target-configuration-details-group-container.tsx b/graph/ui-project-details/src/lib/target-configuration-details-group-container/target-configuration-details-group-container.tsx index 9ccb92267ae1b..6de9568be559b 100644 --- a/graph/ui-project-details/src/lib/target-configuration-details-group-container/target-configuration-details-group-container.tsx +++ b/graph/ui-project-details/src/lib/target-configuration-details-group-container/target-configuration-details-group-container.tsx @@ -3,12 +3,18 @@ import { TargetConfigurationGroupHeader } from '../target-configuration-details- export interface TargetConfigurationGroupContainerProps { targetGroupName: string; targetsNumber: number; + nonAtomizedTarget?: string; + connectedToCloud?: boolean; + nxConnectCallback?: () => void; children: React.ReactNode; } export function TargetConfigurationGroupContainer({ targetGroupName, targetsNumber, + nonAtomizedTarget, + connectedToCloud, + nxConnectCallback, children, }: TargetConfigurationGroupContainerProps) { return ( @@ -16,6 +22,9 @@ export function TargetConfigurationGroupContainer({
diff --git a/graph/ui-project-details/src/lib/target-configuration-details-group-header/target-configuration-details-group-header.stories.tsx b/graph/ui-project-details/src/lib/target-configuration-details-group-header/target-configuration-details-group-header.stories.tsx index b9051371c324d..5d94f388dd9f1 100644 --- a/graph/ui-project-details/src/lib/target-configuration-details-group-header/target-configuration-details-group-header.stories.tsx +++ b/graph/ui-project-details/src/lib/target-configuration-details-group-header/target-configuration-details-group-header.stories.tsx @@ -15,3 +15,21 @@ export const Simple: Story = { targetsNumber: 5, }, }; + +export const AtomizerCloud: Story = { + args: { + targetGroupName: 'Target Group Name', + targetsNumber: 5, + nonAtomizedTarget: 'e2e', + connectedToCloud: true, + }, +}; + +export const AtomizerNoCloud: Story = { + args: { + targetGroupName: 'Target Group Name', + targetsNumber: 5, + nonAtomizedTarget: 'e2e', + connectedToCloud: false, + }, +}; diff --git a/graph/ui-project-details/src/lib/target-configuration-details-group-header/target-configuration-details-group-header.tsx b/graph/ui-project-details/src/lib/target-configuration-details-group-header/target-configuration-details-group-header.tsx index cbe4f6ee2b740..93c7ba4d204ec 100644 --- a/graph/ui-project-details/src/lib/target-configuration-details-group-header/target-configuration-details-group-header.tsx +++ b/graph/ui-project-details/src/lib/target-configuration-details-group-header/target-configuration-details-group-header.tsx @@ -1,25 +1,60 @@ +import { AtomizerTooltip, Tooltip } from '@nx/graph/ui-tooltips'; import { Pill } from '../pill'; +import { Square3Stack3DIcon } from '@heroicons/react/24/outline'; export interface TargetConfigurationGroupHeaderProps { targetGroupName: string; targetsNumber: number; className?: string; + nonAtomizedTarget?: string; + connectedToCloud?: boolean; + nxConnectCallback?: () => void; + showIcon?: boolean; } export const TargetConfigurationGroupHeader = ({ targetGroupName, targetsNumber, + nonAtomizedTarget, + connectedToCloud = true, + nxConnectCallback, className = '', }: TargetConfigurationGroupHeaderProps) => { return ( -
+
{targetGroupName}{' '} + {nonAtomizedTarget && } + {nonAtomizedTarget && ( + + ) as any + } + > + + + + + )}
); }; 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 04755a85e82d1..3a69998748033 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 @@ -4,7 +4,10 @@ import type { ProjectGraphProjectNode } from '@nx/devkit'; import { TargetConfigurationDetailsListItem } from '../target-configuration-details-list-item/target-configuration-details-list-item'; import { TargetConfigurationGroupContainer } from '../target-configuration-details-group-container/target-configuration-details-group-container'; -import { groupTargets } from '../utils/group-targets'; +import { + getNonAtomizedTargetForGroup, + groupTargets, +} from '../utils/group-targets'; import { useMemo } from 'react'; export interface TargetConfigurationGroupListProps { @@ -16,6 +19,8 @@ export interface TargetConfigurationGroupListProps { projectName: string; targetName: string; }) => void; + nxConnectCallback?: () => void; + connectedToCloud?: boolean; className?: string; } @@ -25,7 +30,9 @@ export function TargetConfigurationGroupList({ sourceMap, onRunTarget, onViewInTaskGraph, + nxConnectCallback, className = '', + connectedToCloud, }: TargetConfigurationGroupListProps) { const targetsGroup = useMemo(() => groupTargets(project), [project]); const hasGroups = useMemo(() => { @@ -47,6 +54,12 @@ export function TargetConfigurationGroupList({
    diff --git a/graph/ui-project-details/src/lib/utils/group-targets.ts b/graph/ui-project-details/src/lib/utils/group-targets.ts index 0e7c4b4cd64b7..5ff330bf7bb94 100644 --- a/graph/ui-project-details/src/lib/utils/group-targets.ts +++ b/graph/ui-project-details/src/lib/utils/group-targets.ts @@ -33,3 +33,18 @@ function sortNxReleasePublishLast(a: string, b: string) { if (b === 'nx-release-publish') return -1; return a.localeCompare(b); } + +export function getNonAtomizedTargetForGroup( + project: ProjectGraphProjectNode, + targetGroupName: string +): string | undefined { + const targetWithNonAtomizedEquivalent = project.data.metadata?.targetGroups?.[ + targetGroupName + ]?.find( + (target) => project.data.targets?.[target]?.metadata?.nonAtomizedTarget + ); + return targetWithNonAtomizedEquivalent + ? project.data.targets?.[targetWithNonAtomizedEquivalent]?.metadata + ?.nonAtomizedTarget + : undefined; +} diff --git a/graph/ui-tooltips/src/index.ts b/graph/ui-tooltips/src/index.ts index 2c7d4f05ad36a..643d1d963ed44 100644 --- a/graph/ui-tooltips/src/index.ts +++ b/graph/ui-tooltips/src/index.ts @@ -6,3 +6,4 @@ export * from './lib/tooltip-button'; export * from './lib/property-info-tooltip'; export * from './lib/sourcemap-info-tooltip'; export * from './lib/external-link'; +export * from './lib/atomizer-tooltip'; diff --git a/graph/ui-tooltips/src/lib/atomizer-tooltip.stories.tsx b/graph/ui-tooltips/src/lib/atomizer-tooltip.stories.tsx new file mode 100644 index 0000000000000..532ed8967ee30 --- /dev/null +++ b/graph/ui-tooltips/src/lib/atomizer-tooltip.stories.tsx @@ -0,0 +1,72 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { AtomizerTooltip, AtomizerTooltipProps } from './atomizer-tooltip'; +import { Tooltip } from './tooltip'; + +const meta: Meta = { + component: AtomizerTooltip, + title: 'Tooltips/AtomizerTooltip', +}; + +export default meta; +type Story = StoryObj; + +export const Cloud: Story = { + args: { + connectedToCloud: true, + nonAtomizedTarget: 'e2e', + } as AtomizerTooltipProps, + render: (args) => { + return ( +
    + ) as any} + > +

    Internal Reference

    +
    +
    + ); + }, +}; + +export const NoCloud: Story = { + args: { + connectedToCloud: false, + nonAtomizedTarget: 'e2e', + } as AtomizerTooltipProps, + render: (args) => { + return ( +
    + ) as any} + > +

    Internal Reference

    +
    +
    + ); + }, +}; + +export const NoCloudConsole: Story = { + args: { + connectedToCloud: false, + nonAtomizedTarget: 'e2e', + nxConnectCallback: () => console.log('nxConnectCallback'), + } as AtomizerTooltipProps, + render: (args) => { + return ( +
    + ) as any} + > +

    Internal Reference

    +
    +
    + ); + }, +}; diff --git a/graph/ui-tooltips/src/lib/atomizer-tooltip.tsx b/graph/ui-tooltips/src/lib/atomizer-tooltip.tsx new file mode 100644 index 0000000000000..6b12f6774bb68 --- /dev/null +++ b/graph/ui-tooltips/src/lib/atomizer-tooltip.tsx @@ -0,0 +1,106 @@ +import { NxCloudIcon } from '@nx/graph/ui-icons'; +import { twMerge } from 'tailwind-merge'; + +export interface AtomizerTooltipProps { + connectedToCloud: boolean; + nonAtomizedTarget: string; + nxConnectCallback?: () => void; +} +export function AtomizerTooltip(props: AtomizerTooltipProps) { + return ( +
    +

    + Atomizer +

    +
    +

    + {'Nx '} + + { + ' this potentially slow task into separate tasks for each file. We recommend enabling ' + } + {!props.connectedToCloud && ( + <> + + {' and '} + + )} + + {' to benefit from '} + + {!props.connectedToCloud && ( + <> + {', '} + + + )} + {' and '} + + . Use + + {props.nonAtomizedTarget} + + when running without Nx Agents. +

    +
    + {!props.connectedToCloud && ( +
    +

    + {props.nxConnectCallback ? ( + + ) : ( + + {'Run'} + + nx connect + + {'to connect to Nx Cloud'} + + )} +

    +
    + )} +
    + ); +} + +function Link({ href, text }: { href: string; text: string }) { + return ( + + {text} + + ); +} diff --git a/graph/ui-tooltips/src/lib/property-info-tooltip.tsx b/graph/ui-tooltips/src/lib/property-info-tooltip.tsx index ecc8882c85c30..06e90d9923b41 100644 --- a/graph/ui-tooltips/src/lib/property-info-tooltip.tsx +++ b/graph/ui-tooltips/src/lib/property-info-tooltip.tsx @@ -99,7 +99,7 @@ export function PropertyInfoTooltip({ type }: PropertyInfoTooltipProps) { : '' )} > -

    +

    {propertyInfo.description}

diff --git a/graph/ui-tooltips/src/lib/tooltip.tsx b/graph/ui-tooltips/src/lib/tooltip.tsx index 4af4b8e044f1b..6520a43edf54e 100644 --- a/graph/ui-tooltips/src/lib/tooltip.tsx +++ b/graph/ui-tooltips/src/lib/tooltip.tsx @@ -25,6 +25,7 @@ import { useRole, safePolygon, useTransitionStyles, + FloatingPortal, } from '@floating-ui/react'; export type TooltipProps = HTMLAttributes & { @@ -37,6 +38,7 @@ export type TooltipProps = HTMLAttributes & { buffer?: number; showTooltipArrow?: boolean; strategy?: 'absolute' | 'fixed'; + usePortal?: boolean; }; export function Tooltip({ @@ -49,6 +51,7 @@ export function Tooltip({ strategy = 'absolute', buffer = 0, showTooltipArrow = true, + usePortal = false, }: TooltipProps) { const [isOpen, setIsOpen] = useState(open); const arrowRef = useRef(null); @@ -123,41 +126,49 @@ export function Tooltip({ ...getReferenceProps(), }; + const renderTooltip = () => ( +
+ {showTooltipArrow && ( +
+ )} +
+ {content} +
+
+ ); + return ( <> {!externalReference && !!children ? cloneElement(children, cloneProps) : children} {isOpen && isMounted ? ( -
- {showTooltipArrow && ( -
- )} -
- {content} -
-
+ usePortal ? ( + {renderTooltip()} + ) : ( + renderTooltip() + ) ) : null} ); diff --git a/packages/cypress/src/plugins/plugin.spec.ts b/packages/cypress/src/plugins/plugin.spec.ts index fc811af05478b..a5a6ca6c08b52 100644 --- a/packages/cypress/src/plugins/plugin.spec.ts +++ b/packages/cypress/src/plugins/plugin.spec.ts @@ -306,6 +306,7 @@ describe('@nx/cypress/plugin', () => { ], "metadata": { "description": "Runs Cypress Tests in CI", + "nonAtomizedTarget": "e2e", "technologies": [ "cypress", ], @@ -329,6 +330,7 @@ describe('@nx/cypress/plugin', () => { ], "metadata": { "description": "Runs Cypress Tests in src/test.cy.ts in CI", + "nonAtomizedTarget": "e2e", "technologies": [ "cypress", ], diff --git a/packages/cypress/src/plugins/plugin.ts b/packages/cypress/src/plugins/plugin.ts index 45c0582fa6d94..9467113d6d6bd 100644 --- a/packages/cypress/src/plugins/plugin.ts +++ b/packages/cypress/src/plugins/plugin.ts @@ -270,6 +270,7 @@ async function buildCypressTargets( metadata: { technologies: ['cypress'], description: `Runs Cypress Tests in ${relativeSpecFilePath} in CI`, + nonAtomizedTarget: options.targetName, }, }; dependsOn.push({ @@ -288,6 +289,7 @@ async function buildCypressTargets( metadata: { technologies: ['cypress'], description: 'Runs Cypress Tests in CI', + nonAtomizedTarget: options.targetName, }, }; ciTargetGroup.push(options.ciTargetName); diff --git a/packages/jest/src/plugins/plugin.spec.ts b/packages/jest/src/plugins/plugin.spec.ts index c2d606953c100..27c7ff58d8948 100644 --- a/packages/jest/src/plugins/plugin.spec.ts +++ b/packages/jest/src/plugins/plugin.spec.ts @@ -179,6 +179,7 @@ describe('@nx/jest/plugin', () => { ], "metadata": { "description": "Run Jest Tests in CI", + "nonAtomizedTarget": "test", "technologies": [ "jest", ], @@ -201,6 +202,7 @@ describe('@nx/jest/plugin', () => { ], "metadata": { "description": "Run Jest Tests in src/unit.spec.ts", + "nonAtomizedTarget": "test", "technologies": [ "jest", ], @@ -304,48 +306,48 @@ describe('@nx/jest/plugin', () => { ); expect(results).toMatchInlineSnapshot(` - [ - [ - "proj/jest.config.js", - { - "projects": { - "proj": { - "metadata": undefined, - "root": "proj", - "targets": { - "test": { - "cache": true, - "command": "jest", - "inputs": [ - "default", - "^production", - { - "externalDependencies": [ - "jest", - "some-package", - ], + [ + [ + "proj/jest.config.js", + { + "projects": { + "proj": { + "metadata": undefined, + "root": "proj", + "targets": { + "test": { + "cache": true, + "command": "jest", + "inputs": [ + "default", + "^production", + { + "externalDependencies": [ + "jest", + "some-package", + ], + }, + ], + "metadata": { + "description": "Run Jest Tests", + "technologies": [ + "jest", + ], + }, + "options": { + "cwd": "proj", + }, + "outputs": [ + "{workspaceRoot}/coverage", + ], + }, + }, }, - ], - "metadata": { - "description": "Run Jest Tests", - "technologies": [ - "jest", - ], }, - "options": { - "cwd": "proj", - }, - "outputs": [ - "{workspaceRoot}/coverage", - ], }, - }, - }, - }, - }, - ], - ] - `); + ], + ] + `); } ); }); diff --git a/packages/jest/src/plugins/plugin.ts b/packages/jest/src/plugins/plugin.ts index d25ef6fd42db4..9cc595a5178d3 100644 --- a/packages/jest/src/plugins/plugin.ts +++ b/packages/jest/src/plugins/plugin.ts @@ -237,6 +237,7 @@ async function buildJestTargets( metadata: { technologies: ['jest'], description: 'Run Jest Tests in CI', + nonAtomizedTarget: options.targetName, }, }; targetGroup.push(options.ciTargetName); @@ -258,6 +259,7 @@ async function buildJestTargets( metadata: { technologies: ['jest'], description: `Run Jest Tests in ${relativePath}`, + nonAtomizedTarget: options.targetName, }, }; targetGroup.push(targetName); diff --git a/packages/nx/src/command-line/graph/graph.ts b/packages/nx/src/command-line/graph/graph.ts index 55ecb5e4af357..57f641bfc5af0 100644 --- a/packages/nx/src/command-line/graph/graph.ts +++ b/packages/nx/src/command-line/graph/graph.ts @@ -56,6 +56,7 @@ import { createTaskHasher } from '../../hasher/create-task-hasher'; import { filterUsingGlobPatterns } from '../../hasher/task-hasher'; import { ProjectGraphError } from '../../project-graph/error-types'; +import { isNxCloudUsed } from '../../utils/nx-cloud-utils'; export interface GraphError { message: string; @@ -78,6 +79,7 @@ export interface ProjectGraphClientResponse { exclude: string[]; isPartial: boolean; errors?: GraphError[]; + connectedToCloud?: boolean; } export interface TaskGraphClientResponse { @@ -748,11 +750,14 @@ async function createProjectGraphAndSourceMapClientResponse( let sourceMaps: ConfigurationSourceMaps; let isPartial = false; let errors: GraphError[] | undefined; + let connectedToCloud: boolean | undefined; try { const projectGraphAndSourceMaps = await createProjectGraphAndSourceMapsAsync({ exitOnError: false }); projectGraph = projectGraphAndSourceMaps.projectGraph; sourceMaps = projectGraphAndSourceMaps.sourceMaps; + + connectedToCloud = isNxCloudUsed(readNxJson()); } catch (e) { if (e instanceof ProjectGraphError) { projectGraph = e.getPartialProjectGraph(); @@ -786,7 +791,14 @@ async function createProjectGraphAndSourceMapClientResponse( const hasher = createHash('sha256'); hasher.update( - JSON.stringify({ layout, projects, dependencies, sourceMaps, errors }) + JSON.stringify({ + layout, + projects, + dependencies, + sourceMaps, + errors, + connectedToCloud, + }) ); const hash = hasher.digest('hex'); @@ -816,6 +828,7 @@ async function createProjectGraphAndSourceMapClientResponse( fileMap, isPartial, errors, + connectedToCloud, }, sourceMapResponse: sourceMaps, }; diff --git a/packages/nx/src/config/workspace-json-project-json.ts b/packages/nx/src/config/workspace-json-project-json.ts index 29cad1d7c2cf6..71b35304b3afc 100644 --- a/packages/nx/src/config/workspace-json-project-json.ts +++ b/packages/nx/src/config/workspace-json-project-json.ts @@ -128,6 +128,7 @@ export interface TargetMetadata { [k: string]: any; description?: string; technologies?: string[]; + nonAtomizedTarget?: string; } export interface TargetDependencyConfig { diff --git a/packages/nx/src/utils/nx-cloud-utils.ts b/packages/nx/src/utils/nx-cloud-utils.ts index 8b3a17e2d9ff7..dc2407021eb3d 100644 --- a/packages/nx/src/utils/nx-cloud-utils.ts +++ b/packages/nx/src/utils/nx-cloud-utils.ts @@ -1,8 +1,8 @@ import { NxJsonConfiguration, readNxJson } from '../config/nx-json'; -export function isNxCloudUsed(nxJson: NxJsonConfiguration) { +export function isNxCloudUsed(nxJson: NxJsonConfiguration): boolean { return ( - process.env.NX_CLOUD_ACCESS_TOKEN || + !!process.env.NX_CLOUD_ACCESS_TOKEN || !!nxJson.nxCloudAccessToken || !!Object.values(nxJson.tasksRunnerOptions ?? {}).find( (r) => r.runner == '@nrwl/nx-cloud' || r.runner == 'nx-cloud' diff --git a/packages/playwright/src/plugins/plugin.spec.ts b/packages/playwright/src/plugins/plugin.spec.ts index a431927a7b79a..c257d7c4bc9e7 100644 --- a/packages/playwright/src/plugins/plugin.spec.ts +++ b/packages/playwright/src/plugins/plugin.spec.ts @@ -99,6 +99,7 @@ describe('@nx/playwright/plugin', () => { ], "metadata": { "description": "Runs Playwright Tests in CI", + "nonAtomizedTarget": "e2e", "technologies": [ "playwright", ], @@ -191,6 +192,7 @@ describe('@nx/playwright/plugin', () => { ], "metadata": { "description": "Runs Playwright Tests in CI", + "nonAtomizedTarget": "e2e", "technologies": [ "playwright", ], @@ -273,6 +275,7 @@ describe('@nx/playwright/plugin', () => { ], "metadata": { "description": "Runs Playwright Tests in CI", + "nonAtomizedTarget": "e2e", "technologies": [ "playwright", ], @@ -297,6 +300,7 @@ describe('@nx/playwright/plugin', () => { ], "metadata": { "description": "Runs Playwright Tests in tests/run-me.spec.ts in CI", + "nonAtomizedTarget": "e2e", "technologies": [ "playwright", ], @@ -324,6 +328,7 @@ describe('@nx/playwright/plugin', () => { ], "metadata": { "description": "Runs Playwright Tests in tests/run-me-2.spec.ts in CI", + "nonAtomizedTarget": "e2e", "technologies": [ "playwright", ], diff --git a/packages/playwright/src/plugins/plugin.ts b/packages/playwright/src/plugins/plugin.ts index ef311667e4af5..fa4159d9c7814 100644 --- a/packages/playwright/src/plugins/plugin.ts +++ b/packages/playwright/src/plugins/plugin.ts @@ -215,6 +215,7 @@ async function buildPlaywrightTargets( metadata: { technologies: ['playwright'], description: `Runs Playwright Tests in ${relativeSpecFilePath} in CI`, + nonAtomizedTarget: options.targetName, }, }; dependsOn.push({ @@ -241,6 +242,7 @@ async function buildPlaywrightTargets( metadata: { technologies: ['playwright'], description: 'Runs Playwright Tests in CI', + nonAtomizedTarget: options.targetName, }, }; ciTargetGroup.push(options.ciTargetName);