Skip to content

Commit

Permalink
fix: languageserver filepath on Windows (#1715)
Browse files Browse the repository at this point in the history
Co-authored-by: Rikki Schulte <rikki.schulte@gmail.com>
  • Loading branch information
lanwin and acao committed Nov 28, 2020
1 parent eb2a5f0 commit d2feff9
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 54 deletions.
28 changes: 10 additions & 18 deletions packages/graphql-language-service-server/src/GraphQLCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { parseDocument } from './parseDocument';
import stringToHash from './stringToHash';
import glob from 'glob';
import { LoadConfigOptions } from './types';
import { fileURLToPath, pathToFileURL } from 'url';

// Maximum files to read when processing GraphQL files.
const MAX_READS = 200;
Expand Down Expand Up @@ -105,7 +106,7 @@ export class GraphQLCache implements GraphQLCacheInterface {
getGraphQLConfig = (): GraphQLConfig => this._graphQLConfig;

getProjectForFile = (uri: string): GraphQLProjectConfig => {
return this._graphQLConfig.getProjectForFile(uri.replace('file://', ''));
return this._graphQLConfig.getProjectForFile(fileURLToPath(uri));
};

getFragmentDependencies = async (
Expand Down Expand Up @@ -192,13 +193,7 @@ export class GraphQLCache implements GraphQLCacheInterface {
return this._fragmentDefinitionsCache.get(rootDir) || new Map();
}

const filesFromInputDirs = await this._readFilesFromInputDirs(
rootDir,
projectConfig.documents,
);
const list = filesFromInputDirs.filter(fileInfo =>
projectConfig.match(fileInfo.filePath),
);
const list = await this._readFilesFromInputDirs(rootDir, projectConfig);

const {
fragmentDefinitions,
Expand Down Expand Up @@ -303,13 +298,7 @@ export class GraphQLCache implements GraphQLCacheInterface {
if (this._typeDefinitionsCache.has(rootDir)) {
return this._typeDefinitionsCache.get(rootDir) || new Map();
}
const filesFromInputDirs = await this._readFilesFromInputDirs(
rootDir,
projectConfig.documents,
);
const list = filesFromInputDirs.filter(fileInfo =>
projectConfig.match(fileInfo.filePath),
);
const list = await this._readFilesFromInputDirs(rootDir, projectConfig);
const {
objectTypeDefinitions,
graphQLFileMap,
Expand All @@ -322,9 +311,10 @@ export class GraphQLCache implements GraphQLCacheInterface {

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

if (!documents || documents.length === 0) {
return Promise.resolve([]);
Expand Down Expand Up @@ -371,14 +361,15 @@ export class GraphQLCache implements GraphQLCacheInterface {
.filter(
filePath => typeof globResult.statCache[filePath] === 'object',
)
.filter(filePath => projectConfig.match(filePath))
.map(filePath => {
// @TODO
// 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 = globResult.statCache[filePath] as fs.Stats;
return {
filePath,
filePath: pathToFileURL(filePath).toString(),
mtime: Math.trunc(cacheEntry.mtime.getTime() / 1000),
size: cacheEntry.size,
};
Expand Down Expand Up @@ -706,6 +697,7 @@ export class GraphQLCache implements GraphQLCacheInterface {
const promises = chunk.map(fileInfo =>
this.promiseToReadGraphQLFile(fileInfo.filePath)
.catch(error => {
console.log('pro', error);
/**
* fs emits `EMFILE | ENFILE` error when there are too many
* open files - this can cause some fragment files not to be
Expand Down Expand Up @@ -800,7 +792,7 @@ export class GraphQLCache implements GraphQLCacheInterface {
*/
promiseToReadGraphQLFile = (filePath: Uri): Promise<GraphQLFileInfo> => {
return new Promise((resolve, reject) =>
fs.readFile(filePath, 'utf8', (error, content) => {
fs.readFile(fileURLToPath(filePath), 'utf8', (error, content) => {
if (error) {
reject(error);
return;
Expand Down
8 changes: 6 additions & 2 deletions packages/graphql-language-service-server/src/Logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,12 @@ export class Logger implements VSCodeLogger {
const logMessage = `${timestamp} [${severity}] (pid: ${pid}) graphql-language-service-usage-logs: ${stringMessage}\n`;
// write to the file in tmpdir
fs.appendFile(this._logFilePath, logMessage, _error => {});
this._getOutputStream(severity).write(logMessage, err => {
err && console.error(err);
const processSt =
severity === DIAGNOSTIC_SEVERITY.Error ? process.stderr : process.stdout;
processSt.write(logMessage, err => {
if (err) {
console.error(err);
}
});
}

Expand Down
46 changes: 19 additions & 27 deletions packages/graphql-language-service-server/src/MessageProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import mkdirp from 'mkdirp';
import { readFileSync, existsSync, writeFileSync, writeFile } from 'fs';
import { URL } from 'url';
import { fileURLToPath, pathToFileURL } from 'url';
import * as path from 'path';
import {
CachedContent,
Expand Down Expand Up @@ -128,7 +128,7 @@ export class MessageProcessor {
};
this._tmpDir = tmpDir || tmpdir();
this._tmpDirBase = path.join(this._tmpDir, 'graphql-language-service');
this._tmpUriBase = path.join('file://', this._tmpDirBase);
this._tmpUriBase = pathToFileURL(this._tmpDirBase).toString();
this._loadConfigOptions = loadConfigOptions;
if (
loadConfigOptions.extensions &&
Expand Down Expand Up @@ -577,7 +577,7 @@ export class MessageProcessor {
) {
const uri = change.uri;

const text: string = readFileSync(new URL(uri).pathname).toString();
const text = readFileSync(fileURLToPath(uri), { encoding: 'utf8' });
const contents = this._parser(text, uri);

await this._updateFragmentDefinition(uri, contents);
Expand Down Expand Up @@ -713,18 +713,8 @@ export class MessageProcessor {
);
}
}

return {
// TODO: fix this hack!
// URI is being misused all over this library - there's a link that
// defines how an URI should be structured:
// https://tools.ietf.org/html/rfc3986
// Remove the below hack once the usage of URI is sorted out in related
// libraries.
uri:
res.path.indexOf('file:///') === 0
? res.path
: `file://${path.resolve(res.path)}`,
uri: res.path,
range: defRange,
} as Location;
})
Expand Down Expand Up @@ -838,7 +828,9 @@ export class MessageProcessor {
const isFileUri = existsSync(uri);
let version = 1;
if (isFileUri) {
const schemaUri = 'file://' + path.join(project.dirpath, uri);
const schemaUri = pathToFileURL(
path.join(project.dirpath, uri),
).toString();
const schemaDocument = this._getCachedDocument(schemaUri);

if (schemaDocument) {
Expand All @@ -864,11 +856,12 @@ export class MessageProcessor {
projectTmpPath = path.join(projectTmpPath, appendPath);
}
if (prependWithProtocol) {
return `file://${path.resolve(projectTmpPath)}`;
return pathToFileURL(path.resolve(projectTmpPath)).toString();
} else {
return path.resolve(projectTmpPath);
}
}

async _cacheSchemaFilesForProject(project: GraphQLProjectConfig) {
const schema = project?.schema;
const config = project?.extensions?.languageService;
Expand Down Expand Up @@ -967,8 +960,15 @@ export class MessageProcessor {
if (!document.location || !document.rawSDL) {
return;
}

let filePath = document.location;
if (!path.isAbsolute(filePath)) {
filePath = path.join(project.dirpath, document.location);
}

// build full system URI path with protocol
const uri = 'file://' + path.join(project.dirpath, document.location);
const uri = pathToFileURL(filePath).toString();

// i would use the already existing graphql-config AST, but there are a few reasons we can't yet
const contents = this._parser(document.rawSDL, uri);
if (!contents[0] || !contents[0].query) {
Expand Down Expand Up @@ -1015,11 +1015,7 @@ export class MessageProcessor {
): Promise<void> {
const rootDir = this._graphQLCache.getGraphQLConfig().dirpath;

await this._graphQLCache.updateFragmentDefinition(
rootDir,
new URL(uri).pathname,
contents,
);
await this._graphQLCache.updateFragmentDefinition(rootDir, uri, contents);
}

async _updateObjectTypeDefinition(
Expand All @@ -1028,11 +1024,7 @@ export class MessageProcessor {
): Promise<void> {
const rootDir = this._graphQLCache.getGraphQLConfig().dirpath;

await this._graphQLCache.updateObjectTypeDefinition(
rootDir,
new URL(uri).pathname,
contents,
);
await this._graphQLCache.updateObjectTypeDefinition(rootDir, uri, contents);
}

_getCachedDocument(uri: string): CachedDocumentType | null {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { loadConfig } from 'graphql-config';
import type { DefinitionQueryResult, Outline } from 'graphql-language-service';

import { Logger } from '../Logger';
import { pathToFileURL } from 'url';

const baseConfig = { dirpath: __dirname };

Expand All @@ -36,7 +37,7 @@ describe('MessageProcessor', () => {
loadConfigOptions: { rootDir: __dirname },
});

const queryDir = `${__dirname}/__queries__`;
const queryPathUri = pathToFileURL(`${__dirname}/__queries__`);
const textDocumentTestString = `
{
hero(episode: NEWHOPE){
Expand Down Expand Up @@ -123,7 +124,7 @@ describe('MessageProcessor', () => {
const initialDocument = {
textDocument: {
text: textDocumentTestString,
uri: `${queryDir}/test.graphql`,
uri: `${queryPathUri}/test.graphql`,
version: 0,
},
};
Expand All @@ -146,7 +147,7 @@ describe('MessageProcessor', () => {
});

it('runs completion requests properly', async () => {
const uri = `${queryDir}/test2.graphql`;
const uri = `${queryPathUri}/test2.graphql`;
const query = 'test';
messageProcessor._textDocumentCache.set(uri, {
version: 0,
Expand All @@ -170,7 +171,7 @@ describe('MessageProcessor', () => {
});

it('runs document symbol requests', async () => {
const uri = `${queryDir}/test3.graphql`;
const uri = `${queryPathUri}/test3.graphql`;
const validQuery = `
{
hero(episode: EMPIRE){
Expand Down Expand Up @@ -214,7 +215,7 @@ describe('MessageProcessor', () => {
});

it('properly changes the file cache with the didChange handler', async () => {
const uri = `file://${queryDir}/test.graphql`;
const uri = `${queryPathUri}/test.graphql`;
messageProcessor._textDocumentCache.set(uri, {
version: 1,
contents: [
Expand Down Expand Up @@ -284,7 +285,7 @@ describe('MessageProcessor', () => {
const newDocument = {
textDocument: {
text: validQuery,
uri: `${queryDir}/test3.graphql`,
uri: `${queryPathUri}/test3.graphql`,
version: 1,
},
};
Expand All @@ -306,7 +307,7 @@ describe('MessageProcessor', () => {
};

const result = await messageProcessor.handleDefinitionRequest(test);
await expect(result[0].uri).toEqual(`file://${queryDir}/test3.graphql`);
await expect(result[0].uri).toEqual(`${queryPathUri}/test3.graphql`);
});

it('parseDocument finds queries in tagged templates', async () => {
Expand Down

0 comments on commit d2feff9

Please sign in to comment.