Skip to main content
Glama
nrwl

Nx MCP Server

Official
by nrwl
get-document-links.spec.ts8.69 kB
import { configureJsonLanguageService, getLanguageModelCache, } from '@nx-console/language-server-utils'; import { ClientCapabilities, TextDocument } from 'vscode-json-languageservice'; import { getDocumentLinks } from './get-document-links'; import { X_COMPLETION_TYPE } from '@nx-console/shared-json-schema'; import * as fs from '@nx-console/shared-file-system'; import { normalize } from 'path'; jest.mock( '@nx-console/shared-file-system', (): Partial<typeof fs> => ({ fileExists: jest.fn(() => Promise.resolve(true)), }), ); import * as nxWorkspaceInfo from '@nx-console/shared-nx-workspace-info'; jest.mock('@nx-console/shared-nx-workspace-info'); const languageService = configureJsonLanguageService( { clientCapabilities: ClientCapabilities.LATEST, }, {}, ); const documentMapper = getLanguageModelCache(); const { document, jsonAst } = documentMapper.retrieve( TextDocument.create( 'file:///project.json', 'json', 0, ` { "fileCompletion": "project/src/main.ts", "dirCompletion": "project/src" } `, ), ); it('should get all document links for properties that have a X_COMPLETION_TYPE (file type only)', async () => { const matchingSchemas = await languageService.getMatchingSchemas( document, jsonAst, { type: 'object', properties: { fileCompletion: { type: 'string', [X_COMPLETION_TYPE]: 'file', } as any, dirCompletion: { type: 'string', [X_COMPLETION_TYPE]: 'directory', } as any, }, }, ); const documentLinks = await getDocumentLinks( '/workspace', jsonAst, document, matchingSchemas, ); expect(documentLinks.map((link) => normalize(link.target ?? ''))).toEqual([ normalize('/workspace/project/src/main.ts'), ]); documentMapper.dispose(); }); it('should get document links for interpolated paths', async () => { const { document, jsonAst } = documentMapper.retrieve( TextDocument.create( 'file:///project.json', 'json', 0, ` { "root": "apps/my-app", "interpolatedWorkspace": "{workspaceRoot}/libs/my-lib/src/index.ts", "interpolatedProject": "{projectRoot}/src/main.ts", "interpolatedGlob": "{projectRoot}/**/*.ts" } `, ), ); const matchingSchemas = await languageService.getMatchingSchemas( document, jsonAst, { type: 'object', properties: { interpolatedWorkspace: { type: 'string' }, interpolatedProject: { type: 'string' }, interpolatedGlob: { type: 'string' }, }, }, ); const documentLinks = await getDocumentLinks( '/workspace', jsonAst, document, matchingSchemas, ); expect(documentLinks.map((link) => link.target)).toEqual([ 'file:///workspace/libs/my-lib/src/index.ts', 'file:///workspace/apps/my-app/src/main.ts', ]); documentMapper.dispose(); }); it('should get document links for negated interpolated paths', async () => { const { document, jsonAst } = documentMapper.retrieve( TextDocument.create( 'file:///project.json', 'json', 0, ` { "interpolatedWorkspace": "!{workspaceRoot}/libs/my-lib/src/index.ts", "interpolatedProject": "!{projectRoot}/src/main.ts" } `, ), ); const matchingSchemas = await languageService.getMatchingSchemas( document, jsonAst, { type: 'object', properties: { interpolatedWorkspace: { type: 'string' }, interpolatedProject: { type: 'string' }, }, }, ); const documentLinks = await getDocumentLinks( '/workspace', jsonAst, document, matchingSchemas, ); expect(documentLinks.map((link) => link.target)).toEqual([ 'file:///workspace/libs/my-lib/src/index.ts', 'file:///workspace/apps/my-app/src/main.ts', ]); documentMapper.dispose(); }); describe('project links', () => { const mockProjectGraph = { nodes: { 'my-app': { data: { root: 'apps/my-app', }, }, 'my-lib': { data: { root: 'libs/my-lib', }, }, }, }; beforeEach(() => { jest.clearAllMocks(); (nxWorkspaceInfo.nxWorkspace as jest.Mock).mockResolvedValue({ projectGraph: mockProjectGraph, }); }); it('should create links for valid projects in implicitDependencies', async () => { const documentMapper = getLanguageModelCache(); const { document, jsonAst } = documentMapper.retrieve( TextDocument.create( 'file:///project1.json', 'json', 0, ` { "implicitDependencies": ["my-app", "my-lib"] } `, ), ); const matchingSchemas = await languageService.getMatchingSchemas( document, jsonAst, { type: 'object', properties: { implicitDependencies: { type: 'array', items: { type: 'string', [X_COMPLETION_TYPE]: 'projects', } as any, }, }, }, ); const documentLinks = await getDocumentLinks( '/workspace', jsonAst, document, matchingSchemas, ); expect(documentLinks).toHaveLength(2); expect(documentLinks[0].target).toContain('apps/my-app/project.json'); expect(documentLinks[1].target).toContain('libs/my-lib/project.json'); documentMapper.dispose(); }); it('should handle projects with ! prefix', async () => { const documentMapper = getLanguageModelCache(); const { document, jsonAst } = documentMapper.retrieve( TextDocument.create( 'file:///project2.json', 'json', 0, ` { "implicitDependencies": ["!my-app"] } `, ), ); const matchingSchemas = await languageService.getMatchingSchemas( document, jsonAst, { type: 'object', properties: { implicitDependencies: { type: 'array', items: { type: 'string', [X_COMPLETION_TYPE]: 'projects', } as any, }, }, }, ); const documentLinks = await getDocumentLinks( '/workspace', jsonAst, document, matchingSchemas, ); expect(documentLinks).toHaveLength(1); expect(documentLinks[0].target).toContain('apps/my-app/project.json'); documentMapper.dispose(); }); it('should not create links for invalid projects', async () => { const documentMapper = getLanguageModelCache(); const { document, jsonAst } = documentMapper.retrieve( TextDocument.create( 'file:///project3.json', 'json', 0, ` { "implicitDependencies": ["non-existent-project"] } `, ), ); const matchingSchemas = await languageService.getMatchingSchemas( document, jsonAst, { type: 'object', properties: { implicitDependencies: { type: 'array', items: { type: 'string', [X_COMPLETION_TYPE]: 'projects', } as any, }, }, }, ); const documentLinks = await getDocumentLinks( '/workspace', jsonAst, document, matchingSchemas, ); expect(documentLinks).toHaveLength(0); documentMapper.dispose(); }); it('should link to package.json when project.json does not exist', async () => { (fs.fileExists as jest.Mock).mockImplementation((path: string) => { if (path.includes('project.json')) { return Promise.resolve(false); } if (path.includes('package.json')) { return Promise.resolve(true); } return Promise.resolve(false); }); const documentMapper = getLanguageModelCache(); const { document, jsonAst } = documentMapper.retrieve( TextDocument.create( 'file:///project4.json', 'json', 0, ` { "implicitDependencies": ["my-app"] } `, ), ); const matchingSchemas = await languageService.getMatchingSchemas( document, jsonAst, { type: 'object', properties: { implicitDependencies: { type: 'array', items: { type: 'string', [X_COMPLETION_TYPE]: 'projects', } as any, }, }, }, ); const documentLinks = await getDocumentLinks( '/workspace', jsonAst, document, matchingSchemas, ); expect(documentLinks).toHaveLength(1); expect(documentLinks[0].target).toContain('apps/my-app/package.json'); documentMapper.dispose(); }); });

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