Skip to main content
Glama
nrwl

Nx MCP Server

Official
by nrwl
get-completion-items.spec.ts9.29 kB
import { configureJsonLanguageService, getLanguageModelCache, } from '@nx-console/language-server-utils'; import { CompletionType, EnhancedJsonSchema, } from '@nx-console/shared-json-schema'; import type * as workspace from '@nx-console/shared-nx-workspace-info'; import { vol } from 'memfs'; import { ClientCapabilities, Position, TextDocument, } from 'vscode-json-languageservice'; import { getCompletionItems } from './get-completion-items'; import { NxWorkspace } from '@nx-console/shared-types'; import { normalize } from 'path'; jest.mock( '@nx-console/shared-nx-workspace-info', (): Partial<typeof workspace> => ({ nxWorkspace: jest.fn(() => Promise.resolve<NxWorkspace>({ isLerna: false, isEncapsulatedNx: false, validWorkspaceJson: true, workspacePath: '/', workspaceLayout: { appsDir: '', libsDir: '', }, nxVersion: { major: 0, minor: 0, full: '0.0.0', }, projectGraph: { nodes: { project1: { name: 'project1', type: 'app', data: { root: 'apps/project1', tags: ['tag1'], targets: { build: { executor: 'noop', }, test: { executor: 'noop', }, }, }, }, project2: { name: 'project2', type: 'app', data: { root: 'apps/project2', tags: ['tag2', 'tag3'], targets: { build: { executor: 'noop', configurations: { production: {}, }, }, test: { executor: 'noop', }, lint: { executor: 'noop', }, }, }, }, }, dependencies: {}, }, nxJson: {}, }), ), }), ); // eslint-disable-next-line @typescript-eslint/no-var-requires jest.mock('fs', () => require('memfs').fs); const languageService = configureJsonLanguageService( { clientCapabilities: ClientCapabilities.LATEST, }, {}, ); describe('getCompletionItems', () => { const getTestCompletionItemsFor = async ( text: string, schema?: EnhancedJsonSchema, ) => { const offset = text.indexOf('|'); text = text.substr(0, offset) + text.substr(offset + 1); const document = TextDocument.create( 'file:///project.json', 'json', 0, text, ); const jsonAst = languageService.parseJSONDocument(document); const matchingSchemas = await languageService.getMatchingSchemas( document, jsonAst, schema, ); const items = await getCompletionItems( '/workspace', { major: 15, minor: 0, full: '15.0.0', }, jsonAst, document, matchingSchemas, Position.create(0, offset), ); return { labels: items.map((item) => item.label), details: items.map((item) => item.detail), }; }; beforeEach(() => { vol.fromNestedJSON({ '/workspace': { 'file.js': 'content', './project/src': { 'main.ts': 'content', 'main.js': 'content', }, }, }); }); afterAll(() => { vol.reset(); getLanguageModelCache().dispose(); }); it('should return all completion items without a glob', async () => { const { labels, details } = await getTestCompletionItemsFor( `{"fileCompletion": "|"}`, { type: 'object', properties: { fileCompletion: { type: 'string', 'x-completion-type': CompletionType.file, }, }, }, ); expect(labels.map((l) => normalize(l)).sort()).toEqual( [`"file.js"`, `"project/src/main.js"`, `"project/src/main.ts"`] .map((l) => normalize(l)) .sort(), ); expect(details.map((l) => normalize(l ?? '')).sort()).toEqual( [ '/workspace/file.js', '/workspace/project/src/main.js', '/workspace/project/src/main.ts', ] .map((l) => normalize(l)) .sort(), ); }); it('should be able to use a glob', async () => { const { labels } = await getTestCompletionItemsFor( `{"fileCompletion": "|"}`, { type: 'object', properties: { fileCompletion: { type: 'string', 'x-completion-type': CompletionType.file, 'x-completion-glob': '*.ts', }, }, }, ); expect(labels).toMatchInlineSnapshot(` Array [ "\\"project/src/main.ts\\"", ] `); }); describe('tags', () => { const tagSchema = { type: 'object', properties: { tags: { type: 'array', items: { type: 'string', 'x-completion-type': CompletionType.tags, }, }, }, }; it('should return tags', async () => { const { labels } = await getTestCompletionItemsFor( `{"tags": ["|"]}`, tagSchema, ); expect(labels).toMatchInlineSnapshot(` Array [ "\\"tag1\\"", "\\"tag2\\"", "\\"tag3\\"", ] `); }); it('should filter tags that already exist on the property', async () => { const { labels } = await getTestCompletionItemsFor( `{"tags": ["tag1", "|"]}`, tagSchema, ); expect(labels).toMatchInlineSnapshot(` Array [ "\\"tag2\\"", "\\"tag3\\"", ] `); }); }); describe('projects', () => { const projectSchema = { type: 'object', properties: { projects: { type: 'array', items: { type: 'string', 'x-completion-type': CompletionType.projects, }, }, }, }; it('should return projects', async () => { const { labels } = await getTestCompletionItemsFor( `{"projects": ["|"]}`, projectSchema, ); expect(labels).toMatchInlineSnapshot(` Array [ "\\"project1\\"", "\\"!project1\\"", "\\"project2\\"", "\\"!project2\\"", ] `); }); }); describe('targets', () => { const targetSchema = { type: 'object', properties: { targets: { type: 'array', items: { type: 'string', 'x-completion-type': CompletionType.targets, }, }, targetsWithDeps: { type: 'array', items: { type: 'string', 'x-completion-type': CompletionType.targetsWithDeps, }, }, }, }; it('should return targets', async () => { const { labels } = await getTestCompletionItemsFor( `{"targets": ["|"]}`, targetSchema, ); expect(labels).toMatchInlineSnapshot(` Array [ "\\"build\\"", "\\"test\\"", "\\"lint\\"", ] `); }); it('should return targets', async () => { const { labels } = await getTestCompletionItemsFor( `{"targetsWithDeps": ["|"]}`, targetSchema, ); expect(labels).toMatchInlineSnapshot(` Array [ "\\"^build\\"", "\\"build\\"", "\\"^test\\"", "\\"test\\"", "\\"^lint\\"", "\\"lint\\"", ] `); }); }); describe('targets', () => { const projectWithTargetsSchema = { type: 'object', properties: { projectTargets: { type: 'array', items: { type: 'string', 'x-completion-type': CompletionType.projectTarget, }, }, targets: { type: 'string', 'x-completion-type': CompletionType.projects, }, }, }; it('should return targets', async () => { const { labels } = await getTestCompletionItemsFor( `{"projectTargets": ["|"]}`, projectWithTargetsSchema, ); expect(labels).toMatchInlineSnapshot(` Array [ "\\"project1:build\\"", "\\"project1:test\\"", "\\"project2:build\\"", "\\"project2:build:production\\"", "\\"project2:test\\"", "\\"project2:lint\\"", ] `); }); it('should return completion-type results before trying default implementations', async () => { const { labels } = await getTestCompletionItemsFor( `{"targets": "|"}`, projectWithTargetsSchema, ); expect(labels).toMatchInlineSnapshot(` Array [ "\\"project1\\"", "\\"!project1\\"", "\\"project2\\"", "\\"!project2\\"", ] `); }); }); });

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