Skip to main content
Glama
nrwl

Nx MCP Server

Official
by nrwl
atomized-file-codelens-provider.ts5.44 kB
import { getNxWorkspacePath } from '@nx-console/vscode-configuration'; import { getNxWorkspace } from '@nx-console/vscode-nx-workspace'; import { NxCodeLensProvider, registerCodeLensProvider, createProjectTargetString, } from '@nx-console/vscode-utils'; import { onWorkspaceRefreshed } from '@nx-console/vscode-lsp-client'; import { join } from 'path'; import { CallExpression, createSourceFile, ExpressionStatement, Identifier, isImportDeclaration, Node, ScriptTarget, SyntaxKind, } from 'typescript'; import { CancellationToken, CodeLens, Event, EventEmitter, ExtensionContext, ProviderResult, Range, TextDocument, } from 'vscode'; import { CODELENS_RUN_TARGET_COMMAND } from './config-file-codelens-provider'; export class AtomizedFileCodelensProvider implements NxCodeLensProvider { constructor( public workspaceRoot: string, public sourceFilesToAtomizedTargetsMap: Record< string, [project: string, target: string] >, ) {} CODELENS_PATTERN = { scheme: 'file', }; 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<CodeLens[]> { const path = document.uri.fsPath; if (!this.sourceFilesToAtomizedTargetsMap[path]) { return []; } const [project, target] = this.sourceFilesToAtomizedTargetsMap[path]; if (project && target) { const location = this.getCodeLensLocation(document); return [ new CodeLens(location, { title: `$(play) Run ${createProjectTargetString(project, target)} via nx`, command: CODELENS_RUN_TARGET_COMMAND, arguments: [project, target], }), ]; } } private getCodeLensLocation(document: TextDocument): Range { try { if (['typescript', 'javascript'].includes(document.languageId)) { const configFile = createSourceFile( document.fileName, document.getText(), { languageVersion: ScriptTarget.Latest, }, ); let firstNonImportNode: Node | undefined = undefined; for (const node of configFile.statements) { if (node.kind === SyntaxKind.ExpressionStatement) { const expr = (node as ExpressionStatement).expression; if (expr.kind === SyntaxKind.CallExpression) { const call = expr as CallExpression; if ( call.expression.kind === SyntaxKind.Identifier && (call.expression as Identifier).text === 'describe' ) { const pos = document.positionAt(node.getStart(configFile)); return new Range(pos, pos); } } } if (!firstNonImportNode && !isImportDeclaration(node)) { firstNonImportNode = node; } } if (firstNonImportNode) { const pos = document.positionAt( firstNonImportNode.getStart(configFile), ); return new Range(pos, pos); } } return new Range(0, 0, 0, 0); } catch (e) { return new Range(0, 0, 0, 0); } } static async register(context: ExtensionContext) { const workspacePath = getNxWorkspacePath(); const sourceFilesToAtomizedTargetsMap = await getSourceFilesToAtomizedTargetsMap(workspacePath); const provider = new AtomizedFileCodelensProvider( workspacePath, sourceFilesToAtomizedTargetsMap, ); context.subscriptions.push( onWorkspaceRefreshed(async () => { const updatedWorkspacePath = getNxWorkspacePath(); const updatedSourceFilesToAtomizedTargetsMap = await getSourceFilesToAtomizedTargetsMap(updatedWorkspacePath); provider.workspaceRoot = updatedWorkspacePath; provider.sourceFilesToAtomizedTargetsMap = updatedSourceFilesToAtomizedTargetsMap; provider.refresh(); }), ); registerCodeLensProvider(provider); } } export async function getSourceFilesToAtomizedTargetsMap( workspacePath: string, ): Promise<Record<string, [project: string, target: string]>> { const sourceFilesToAtomizedTargetsMap: Record< string, [project: string, target: string] > = {}; const nxWorkspace = await getNxWorkspace(); if (!nxWorkspace) { return {}; } const { projectGraph } = nxWorkspace; for (const projectNode of Object.values(projectGraph.nodes)) { const targetGroups = projectNode.data.metadata?.targetGroups; if (!targetGroups) { continue; } for (const targetGroup of Object.values(targetGroups)) { const atomizerRootTarget = targetGroup.find( (target) => projectNode.data.targets?.[target]?.metadata?.nonAtomizedTarget, ); if (!atomizerRootTarget) { continue; } for (const target of targetGroup) { if (target === atomizerRootTarget) { continue; } const fileName = join( workspacePath, projectNode.data.root, target.replace(`${atomizerRootTarget}--`, ''), ); sourceFilesToAtomizedTargetsMap[fileName] = [projectNode.name, target]; } } } return sourceFilesToAtomizedTargetsMap; }

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