Skip to content

Commit

Permalink
feat: custom config baseDir, embedded fragment def offsets (#1651)
Browse files Browse the repository at this point in the history
- requires using createConnection() strategy
- extension settings on server side for the first time
- allow graphql-config loadConnfig() baseDir to be configured
- properly calculate offsets for inline operation fragment defs
  • Loading branch information
acao committed Aug 26, 2020
1 parent f779aa3 commit e8dc958
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 43 deletions.
6 changes: 2 additions & 4 deletions packages/graphql-language-service-server/src/GraphQLCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,8 @@ export async function getGraphQLCache({
loadConfigOptions: LoadConfigOptions;
config?: GraphQLConfig;
}): Promise<GraphQLCacheInterface> {
let graphQLConfig;
if (config) {
graphQLConfig = config;
} else {
let graphQLConfig = config;
if (!graphQLConfig) {
graphQLConfig = await loadConfig(loadConfigOptions);
}
return new GraphQLCache({
Expand Down
7 changes: 4 additions & 3 deletions packages/graphql-language-service-server/src/Logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,10 @@ export class Logger implements VSCodeLogger {
const logMessage = `${timestamp} [${severity}] (pid: ${pid}) graphql-language-service-usage-logs: ${message}\n\n`;
// write to the file in tmpdir
fs.appendFile(this._logFilePath, logMessage, _error => {});
// const processSt = (severity === DIAGNOSTIC_SEVERITY.Error) ? process.stderr : process.stdout
process.stderr.write(logMessage, _err => {
// console.error(err);
const processSt =
severity === DIAGNOSTIC_SEVERITY.Error ? process.stderr : process.stdout;
processSt.write(logMessage, err => {
console.error(err);
});
}
}
Expand Down
112 changes: 94 additions & 18 deletions packages/graphql-language-service-server/src/MessageProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,16 @@ import type {
DocumentSymbolParams,
SymbolInformation,
WorkspaceSymbolParams,
IConnection,
} from 'vscode-languageserver';

import type { UnnormalizedTypeDefPointer } from '@graphql-tools/load';

import { getGraphQLCache } from './GraphQLCache';
import { parseDocument } from './parseDocument';
import { parseDocument, DEFAULT_SUPPORTED_EXTENSIONS } from './parseDocument';

import { Logger } from './Logger';
import { printSchema } from 'graphql';
import { printSchema, visit, parse, FragmentDefinitionNode } from 'graphql';
import { tmpdir } from 'os';
import { GraphQLExtensionDeclaration } from 'graphql-config';
import type { LoadConfigOptions } from './types';
Expand All @@ -74,6 +75,7 @@ type CachedDocumentType = {
};

export class MessageProcessor {
_connection: IConnection;
_graphQLCache!: GraphQLCache;
_graphQLConfig: GraphQLConfig | undefined;
_languageService!: GraphQLLanguageService;
Expand All @@ -88,6 +90,7 @@ export class MessageProcessor {
_tmpDirBase: string;
_loadConfigOptions: LoadConfigOptions;
_schemaCacheInit: boolean = false;
_rootPath: string = process.cwd();

constructor({
logger,
Expand All @@ -97,6 +100,7 @@ export class MessageProcessor {
config,
parser,
tmpDir,
connection,
}: {
logger: Logger;
fileExtensions: string[];
Expand All @@ -105,7 +109,9 @@ export class MessageProcessor {
config?: GraphQLConfig;
parser?: typeof parseDocument;
tmpDir?: string;
connection: IConnection;
}) {
this._connection = connection;
this._textDocumentCache = new Map();
this._isInitialized = false;
this._willShutdown = false;
Expand All @@ -130,6 +136,12 @@ export class MessageProcessor {
mkdirp(this._tmpDirBase);
}
}
get connection(): IConnection {
return this._connection;
}
set connection(connection: IConnection) {
this._connection = connection;
}

async handleInitializeRequest(
params: InitializeParams,
Expand All @@ -151,15 +163,25 @@ export class MessageProcessor {
definitionProvider: true,
textDocumentSync: 1,
hoverProvider: true,
workspace: {
workspaceFolders: {
supported: true,
changeNotifications: true,
},
},
},
};

const rootPath = configDir ? configDir.trim() : params.rootPath;
if (!rootPath) {
throw new Error(
'`--configDir` option or `rootPath` argument is required.',
this._rootPath = configDir
? configDir.trim()
: params.rootPath || this._rootPath;
if (!this._rootPath) {
this._logger.warn(
'no rootPath configured in extension or server, defaulting to cwd',
);
}
this._loadConfigOptions.rootDir = this._rootPath;

this._graphQLCache = await getGraphQLCache({
parser: this._parser,
loadConfigOptions: this._loadConfigOptions,
Expand Down Expand Up @@ -192,6 +214,21 @@ export class MessageProcessor {
return null;
}

const settings = await this._connection.workspace.getConfiguration({
section: 'graphql-config',
});
if (settings.rootDir && settings.rootDir !== this._rootPath) {
this._rootPath = settings.rootDir;
this._loadConfigOptions.rootDir = settings.rootDir;

this._graphQLCache = await getGraphQLCache({
parser: this._parser,
loadConfigOptions: this._loadConfigOptions,
config: this._graphQLConfig,
});
this._languageService = new GraphQLLanguageService(this._graphQLCache);
}

if (!params || !params.textDocument) {
throw new Error('`textDocument` argument is required.');
}
Expand Down Expand Up @@ -579,9 +616,9 @@ export class MessageProcessor {
return [];
}

const { query, range } = found;
if (range) {
position.line -= range.start.line;
const { query, range: parentRange } = found;
if (parentRange) {
position.line -= parentRange.start.line;
}

const result = await this._languageService.getDefinition(
Expand All @@ -590,9 +627,42 @@ export class MessageProcessor {
textDocument.uri,
);

const inlineFragments: string[] = [];

visit(
parse(query, {
allowLegacySDLEmptyFields: true,
allowLegacySDLImplementsInterfaces: true,
}),
{
FragmentDefinition: (node: FragmentDefinitionNode) => {
inlineFragments.push(node.name.value);
},
},
);

const formatted = result
? result.definitions.map(res => {
const defRange = res.range as Range;

if (parentRange && res.name) {
const isInline = inlineFragments.includes(res.name);
const isEmbedded = DEFAULT_SUPPORTED_EXTENSIONS.includes(
path.extname(textDocument.uri),
);
if (isInline && isEmbedded) {
const vOffset = parentRange.start.line;
defRange.setStart(
(defRange.start.line += vOffset),
defRange.start.character,
);
defRange.setEnd(
(defRange.end.line += vOffset),
defRange.end.character,
);
}
}

return {
// TODO: fix this hack!
// URI is being misused all over this library - there's a link that
Expand All @@ -605,7 +675,7 @@ export class MessageProcessor {
? res.path
: `file://${path.resolve(res.path)}`,
range: defRange,
};
} as Location;
})
: [];

Expand Down Expand Up @@ -693,6 +763,7 @@ export class MessageProcessor {

return [];
}

_getTextDocuments() {
return Array.from(this._textDocumentCache);
}
Expand Down Expand Up @@ -734,8 +805,8 @@ export class MessageProcessor {
appendPath?: string,
) {
const baseDir = this._graphQLCache.getGraphQLConfig().dirpath;
const baseName = path.basename(baseDir);
const basePath = path.join(this._tmpDirBase, baseName);
const workspaceName = path.basename(baseDir);
const basePath = path.join(this._tmpDirBase, workspaceName);
let projectTmpPath = path.join(basePath, 'projects', project.name);
if (!existsSync(projectTmpPath)) {
mkdirp(projectTmpPath);
Expand All @@ -744,9 +815,10 @@ export class MessageProcessor {
projectTmpPath = path.join(projectTmpPath, appendPath);
}
if (prependWithProtocol) {
projectTmpPath = `file://${projectTmpPath}`;
return `file://${path.resolve(projectTmpPath)}`;
} else {
return path.resolve(projectTmpPath);
}
return projectTmpPath;
}
async _cacheSchemaFilesForProject(project: GraphQLProjectConfig) {
const schema = project?.schema;
Expand Down Expand Up @@ -789,23 +861,27 @@ export class MessageProcessor {
try {
const schema = await this._graphQLCache.getSchema(project.name);
if (schema) {
const schemaText = printSchema(schema, {
let schemaText = printSchema(schema, {
commentDescriptions: true,
});
// file:// protocol path
const uri = this._getTmpProjectPath(project, true, 'schema.graphql');
const uri = this._getTmpProjectPath(
project,
true,
'generated-schema.graphql',
);

// no file:// protocol for fs.writeFileSync()
const fsPath = this._getTmpProjectPath(
project,
false,
'schema.graphql',
'generated-schema.graphql',
);
schemaText = `# This is an automatically generated representation of your schema.\n# Any changes to this file will be overwritten and will not be\n# reflected in the resulting GraphQL schema\n\n${schemaText}`;

const cachedSchemaDoc = this._getCachedDocument(uri);

if (!cachedSchemaDoc) {
console.log({ fsPath });
await writeFileAsync(fsPath, schemaText, {
encoding: 'utf-8',
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ import type { DefinitionQueryResult, Outline } from 'graphql-language-service';
import { Logger } from '../Logger';

const baseConfig = { dirpath: __dirname };

describe('MessageProcessor', () => {
const logger = new Logger(tmpdir());
const messageProcessor = new MessageProcessor({
// @ts-ignore
connection: {},
logger,
fileExtensions: ['js'],
graphqlFileExtensions: ['graphql'],
Expand Down Expand Up @@ -103,6 +106,17 @@ describe('MessageProcessor', () => {
},
};
});
// @ts-ignore
messageProcessor._connection = {
// @ts-ignore
get workspace() {
return {
getConfiguration: async () => {
return [{}];
},
};
},
};

const initialDocument = {
textDocument: {
Expand Down Expand Up @@ -247,6 +261,7 @@ describe('MessageProcessor', () => {

// modified to work with jest.mock() of WatchmanClient
it('runs definition requests', async () => {
jest.setTimeout(10000);
const validQuery = `
{
hero(episode: EMPIRE){
Expand Down

0 comments on commit e8dc958

Please sign in to comment.