Skip to main content
Glama
nrwl

Nx MCP Server

Official
by nrwl
project-details-codelens-provider.ts7.48 kB
import { getNxWorkspacePath } from '@nx-console/vscode-configuration'; import { onWorkspaceRefreshed } from '@nx-console/vscode-lsp-client'; import { getProperties, getPropertyName, } from '@nx-console/vscode-nx-config-decoration'; import { getNxWorkspace, getProjectByPath, } from '@nx-console/vscode-nx-workspace'; import { CliTaskProvider } from '@nx-console/vscode-tasks'; import { getTelemetry } from '@nx-console/vscode-telemetry'; import { NxCodeLensProvider, registerCodeLensProvider, createProjectTargetString, } from '@nx-console/vscode-utils'; import type { ProjectConfiguration } from 'nx/src/devkit-exports'; import { parseJsonText } from 'typescript'; import { CancellationToken, CodeLens, commands, Event, EventEmitter, ExtensionContext, Position, ProviderResult, QuickPickItem, QuickPickItemKind, Range, TextDocument, ThemeIcon, window, } from 'vscode'; const OPEN_QUICKPICK_COMMAND = 'nxConsole.project-details.open-quickpick'; export class ProjectDetailsCodelensProvider implements NxCodeLensProvider { CODELENS_PATTERN = { pattern: '**/{package,project}.json' }; constructor(private workspaceRoot: string) {} private changeEvent = new EventEmitter<void>(); public get onDidChangeCodeLenses(): Event<void> { return this.changeEvent.event; } public refresh(): void { this.changeEvent.fire(); } provideCodeLenses( document: TextDocument, token: CancellationToken, ): ProviderResult<(NxTargetsCodelens | ViewProjectDetailsCodelens)[]> { try { const codelenses = []; const codelensLocation = this.getCodelensLocation(document); if (document.fileName.endsWith('project.json')) { codelenses.push( new ViewProjectDetailsCodelens( new Range(codelensLocation, codelensLocation), document.fileName, ), ); } codelenses.push( new NxTargetsCodelens( new Range(codelensLocation, codelensLocation), document.fileName, ), ); return codelenses; } catch (e) { return []; } } async resolveCodeLens?( codeLens: NxTargetsCodelens | ViewProjectDetailsCodelens, token: CancellationToken, ): Promise<NxTargetsCodelens | ViewProjectDetailsCodelens | undefined> { const project = await getProjectByPath(codeLens.filePath); const nxWorkspace = await getNxWorkspace(); const errors = nxWorkspace?.errors; const isPartial = nxWorkspace?.isPartial; const hasProjects = Object.keys(nxWorkspace?.projectGraph.nodes ?? {}).length > 0; const projectGraphFailed = errors && errors.length > 0 && (!isPartial || !hasProjects); if (isNxTargetsCodelens(codeLens)) { if (!project) { if (projectGraphFailed) { return { ...codeLens, command: { command: 'nx.project-details.openToSide', title: `Project graph computation failed. Click to see Details.`, }, }; } else { return { ...codeLens, command: { command: 'nx.run.target', title: `$(play) Run Nx Targets`, }, }; } } let targetsString = Object.keys(project?.targets ?? {}).join(', '); if (targetsString.length > 50) { targetsString = targetsString.slice(0, 50 - 3) + '...'; } return { ...codeLens, command: { command: targetsString ? OPEN_QUICKPICK_COMMAND : 'nx.project-details.openToSide', title: targetsString ? `$(play) Nx Targets: ${targetsString}` : '$(open-preview) Open Project Details View', arguments: [project], }, }; } else { if (!project && projectGraphFailed) { return { ...codeLens, command: { command: 'nx.project-details.openToSide', title: `$(error)`, }, }; } return { ...codeLens, command: { command: 'nx.project-details.openToSide', title: `$(open-preview) View Project Details`, }, }; } } getCodelensLocation(document: TextDocument): Position { const jsonFile = parseJsonText(document.fileName, document.getText()); const properties = getProperties(jsonFile.statements[0].expression); if (document.fileName.endsWith('project.json')) { const targetsProperty = properties?.find( (prop) => getPropertyName(prop) === 'targets', ); if (targetsProperty) { return document.positionAt(targetsProperty.getStart(jsonFile)); } return new Position(1, 1); } else { const nxProperty = properties?.find( (prop) => getPropertyName(prop) === 'nx', ); if (nxProperty) { return document.positionAt(nxProperty.getStart(jsonFile)); } const scriptsProperty = properties?.find( (prop) => getPropertyName(prop) === 'scripts', ); if (scriptsProperty) { return document.positionAt(scriptsProperty.getStart(jsonFile)); } return new Position(1, 1); } } static async register(context: ExtensionContext) { const workspaceRoot = getNxWorkspacePath(); const codeLensProvider = new ProjectDetailsCodelensProvider(workspaceRoot); registerCodeLensProvider(codeLensProvider); context.subscriptions.push( commands.registerCommand(OPEN_QUICKPICK_COMMAND, (project) => { showProjectDetailsQuickpick(project); }), onWorkspaceRefreshed(() => codeLensProvider.refresh()), ); } } function showProjectDetailsQuickpick(project: ProjectConfiguration) { getTelemetry().logUsage('misc.open-project-details-codelens'); const quickPick = window.createQuickPick(); const targetItems: QuickPickItem[] = Object.entries( project.targets ?? {}, ).map(([name, target]) => ({ label: name, description: target.command ?? target.options?.command ?? target.executor, iconPath: new ThemeIcon('play'), })); const openProjectDetailsItem: QuickPickItem = { label: 'View Project Details', iconPath: new ThemeIcon('open-preview'), }; quickPick.items = [ ...targetItems, { label: '', kind: QuickPickItemKind.Separator }, openProjectDetailsItem, ]; quickPick.canSelectMany = false; quickPick.placeholder = `Select action for ${project.name}`; quickPick.show(); quickPick.onDidAccept(() => { const selectedItem = quickPick.selectedItems[0]; if (selectedItem === openProjectDetailsItem) { commands.executeCommand('nx.project-details.openToSide'); } else { CliTaskProvider.instance.executeTask({ command: 'run', positional: createProjectTargetString(project.name, selectedItem.label), flags: [], }); } quickPick.hide(); }); } class NxTargetsCodelens extends CodeLens { type = 'nx-targets-codelens' as const; constructor( range: Range, public filePath: string, ) { super(range); } } class ViewProjectDetailsCodelens extends CodeLens { type = 'view-project-details-codelens' as const; constructor( range: Range, public filePath: string, ) { super(range); } } function isNxTargetsCodelens( codelens: NxTargetsCodelens | ViewProjectDetailsCodelens, ): codelens is NxTargetsCodelens { return codelens.type === 'nx-targets-codelens'; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/nrwl/nx-console'

If you have feedback or need assistance with the MCP directory API, please join our Discord server