Skip to content

Commit

Permalink
feat: graphql-config@3 support in lsp server (#1616)
Browse files Browse the repository at this point in the history
- adopt `documents` instead of `includes`, and other `graphql-config@3` features
- pre-cache schema on load and on `getDefinition`
- load schema from `project.getSchema()` by default or from individual files with config
- pre-cache all files in `documents` on initialization, which can be optionally disabled.
  • Loading branch information
acao committed Aug 8, 2020
1 parent 0fc7c7c commit 27cd185
Show file tree
Hide file tree
Showing 10 changed files with 626 additions and 328 deletions.
3 changes: 2 additions & 1 deletion packages/graphql-language-service-cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import yargs from 'yargs';
import client from './client';

import { Logger, startServer } from 'graphql-language-service-server';
import { tmpdir } from 'os';

const { argv } = yargs
.usage(
Expand Down Expand Up @@ -128,7 +129,7 @@ switch (command) {
try {
startServer(options);
} catch (error) {
const logger = new Logger();
const logger = new Logger(tmpdir());
logger.error(error);
}
break;
Expand Down
2 changes: 1 addition & 1 deletion packages/graphql-language-service-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"dependencies": {
"@babel/parser": "^7.9.0",
"glob": "^7.1.2",
"graphql-config": "^3.0.2",
"graphql-config": "^3.0.3",
"graphql-language-service": "^3.0.1",
"graphql-language-service-utils": "^2.4.1",
"nullthrows": "^1.0.0",
Expand Down
61 changes: 32 additions & 29 deletions packages/graphql-language-service-server/src/GraphQLCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
import { parseDocument } from './parseDocument';
import stringToHash from './stringToHash';
import glob from 'glob';
import { GraphQLExtensionDeclaration } from 'graphql-config/extension';

// Maximum files to read when processing GraphQL files.
const MAX_READS = 200;
Expand All @@ -55,17 +56,23 @@ const {
export async function getGraphQLCache(
configDir: Uri,
parser: typeof parseDocument,
extensions?: Array<(config: GraphQLConfig) => GraphQLConfig>,
extensions: GraphQLExtensionDeclaration[] = [],
config?: GraphQLConfig,
fileExtensions: string[] = [],
// loadConfigOptions?: Parameters<typeof loadConfig>,
): Promise<GraphQLCacheInterface> {
let graphQLConfig =
config ?? ((await loadConfig({ rootDir: configDir })) as GraphQLConfig);
if (extensions && extensions.length > 0) {
for await (const extension of extensions) {
graphQLConfig = await extension(graphQLConfig);
}
}
return new GraphQLCache(configDir, graphQLConfig, parser);
const graphQLConfig =
config ??
(await loadConfig({
rootDir: configDir,
extensions,
}));
return new GraphQLCache(
configDir,
graphQLConfig as GraphQLConfig,
parser,
fileExtensions,
);
}

export class GraphQLCache implements GraphQLCacheInterface {
Expand All @@ -77,11 +84,13 @@ export class GraphQLCache implements GraphQLCacheInterface {
_fragmentDefinitionsCache: Map<Uri, Map<string, FragmentInfo>>;
_typeDefinitionsCache: Map<Uri, Map<string, ObjectTypeInfo>>;
_parser: typeof parseDocument;
_fileExtensions: string[];

constructor(
configDir: Uri,
graphQLConfig: GraphQLConfig,
parser: typeof parseDocument,
fileExtensions: string[],
) {
this._configDir = configDir;
this._graphQLConfig = graphQLConfig;
Expand All @@ -91,6 +100,7 @@ export class GraphQLCache implements GraphQLCacheInterface {
this._typeDefinitionsCache = new Map();
this._typeExtensionMap = new Map();
this._parser = parser;
this._fileExtensions = fileExtensions;
}

getGraphQLConfig = (): GraphQLConfig => this._graphQLConfig;
Expand Down Expand Up @@ -181,11 +191,7 @@ export class GraphQLCache implements GraphQLCacheInterface {

const filesFromInputDirs = await this._readFilesFromInputDirs(
rootDir,
projectConfig.include instanceof Array
? projectConfig.include
: projectConfig.include
? [projectConfig.include]
: [],
projectConfig.documents,
);
const list = filesFromInputDirs.filter(fileInfo =>
projectConfig.match(fileInfo.filePath),
Expand Down Expand Up @@ -296,11 +302,7 @@ export class GraphQLCache implements GraphQLCacheInterface {
}
const filesFromInputDirs = await this._readFilesFromInputDirs(
rootDir,
projectConfig.include instanceof Array
? projectConfig.include
: projectConfig.include
? [projectConfig.include]
: [],
projectConfig.documents,
);
const list = filesFromInputDirs.filter(fileInfo =>
projectConfig.match(fileInfo.filePath),
Expand All @@ -317,21 +319,24 @@ export class GraphQLCache implements GraphQLCacheInterface {

_readFilesFromInputDirs = (
rootDir: string,
includes: string[],
documents: GraphQLProjectConfig['documents'],
): Promise<Array<GraphQLFileMetadata>> => {
let pattern: string;

if (includes.length === 0) {
if (!documents || documents.length === 0) {
return Promise.resolve([]);
}

// See https://github.com/graphql/graphql-language-service/issues/221
// for details on why special handling is required here for the
// includes.length === 1 case.
if (includes.length === 1) {
pattern = includes[0];
// documents.length === 1 case.
if (typeof documents === 'string') {
pattern = documents;
} else if (documents.length === 1) {
// @ts-ignore
pattern = documents[0];
} else {
pattern = `{${includes.join(',')}}`;
pattern = `{${documents.join(',')}}`;
}

return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -368,9 +373,7 @@ export class GraphQLCache implements GraphQLCacheInterface {
// so we have to force this here
// becase glob's DefinatelyTyped doesn't use fs.Stats here though
// the docs indicate that is what's there :shrug:
const cacheEntry: fs.Stats = globResult.statCache[
filePath
] as fs.Stats;
const cacheEntry = globResult.statCache[filePath] as fs.Stats;
return {
filePath,
mtime: Math.trunc(cacheEntry.mtime.getTime() / 1000),
Expand Down Expand Up @@ -804,7 +807,7 @@ export class GraphQLCache implements GraphQLCacheInterface {
let queries: CachedContent[] = [];
if (content.trim().length !== 0) {
try {
queries = this._parser(content, filePath);
queries = this._parser(content, filePath, this._fileExtensions);
if (queries.length === 0) {
// still resolve with an empty ast
resolve({
Expand Down
4 changes: 2 additions & 2 deletions packages/graphql-language-service-server/src/Logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ export class Logger implements VSCodeLogger {
_logFilePath: string;
_stream: fs.WriteStream | null;

constructor() {
const dir = join(os.tmpdir(), 'graphql-language-service-logs');
constructor(tmpDir?: string) {
const dir = join(tmpDir || os.tmpdir(), 'graphql-language-service-logs');
try {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
Expand Down
Loading

0 comments on commit 27cd185

Please sign in to comment.