Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ coverage/
dist/
esm/
out/

# vscode
*.vsix
*.tsbuildinfo
.vscode-test

yarn-1.18.0.js
*.orig
Expand Down
27 changes: 27 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,33 @@
"sourceMaps": true,
"preLaunchTask": "watch-vscode"
},
{
"outputCapture": "console",
"name": "Extension Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"timeout": 30000,
"autoAttachChildProcesses": true,
"serverReadyAction": {
"action": "openExternally"
},
"args": [
"${workspaceFolder}/packages/vscode-graphql/fixtures",
"${workspaceFolder}/packages/vscode-graphql/fixtures/src/queries/query.ts",
"--extensionDevelopmentPath=${workspaceFolder}/packages/vscode-graphql",
"--extensionTestsPath=${workspaceFolder}/packages/vscode-graphql/dist/test/index",
"--disable-workspace-trust",
"--no-sandbox",
"--disable-gpu-sandbox",
"--disable-extensions"
],
"outFiles": [
"${workspaceFolder}/packages/vscode-graphql/out/extension.js"
],
"trace": true,
"preLaunchTask": "watch-vscode"
},
{
"type": "node",
"name": "jest watch",
Expand Down
9 changes: 1 addition & 8 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,17 @@
"type": "npm",
"script": "watch-vscode",
"problemMatcher": ["$tsc-watch"],
"isBackground": true,
"presentation": {
"reveal": "always"
},
"group": {
"kind": "build",
"isDefault": true
}
},

{
"label": "watch-vscode-exec",
"type": "npm",
"script": "watch-vscode-exec",
"problemMatcher": ["$tsc-watch"],
"isBackground": true,
"presentation": {
"reveal": "always"
},
"group": {
"kind": "build",
"isDefault": true
Expand Down
8 changes: 3 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
"workspaces": {
"packages": [
"packages/*",
"examples/monaco-graphql-webpack",
"examples/monaco-graphql-nextjs",
"examples/monaco-graphql-react-vite"
"examples/monaco-graphql-webpack"
]
},
"lint-staged": {
Expand Down Expand Up @@ -47,7 +45,7 @@
"build:packages": "yarn tsc",
"build:watch": "yarn tsc --watch",
"watch": "yarn build:watch",
"watch-vscode": "yarn workspace vscode-graphql run compile",
"watch-vscode": "yarn tsc && yarn workspace vscode-graphql run compile",
"watch-vscode-exec": "yarn workspace vscode-graphql-execution run compile",
"check": "yarn tsc --noEmit",
"cypress-open": "yarn workspace graphiql cypress-open",
Expand Down Expand Up @@ -128,6 +126,7 @@
"eslint-plugin-unicorn": "^47.0.0",
"execa": "^6.0.0",
"express": "^4.18.2",
"esbuild": "0.15.10",
"fetch-mock": "6.5.2",
"husky": "^4.2.3",
"jest": "^27.5.1",
Expand All @@ -139,7 +138,6 @@
"ts-jest": "^27.1.5",
"typedoc": "^0.19.2",
"typescript": "^4.6.3",
"vitest": "^0.32.2",
"wsrun": "^5.2.4"
}
}
141 changes: 86 additions & 55 deletions packages/graphql-language-service-server/src/GraphQLCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,26 @@ import { CodeFileLoader } from '@graphql-tools/code-file-loader';

const LanguageServiceExtension: GraphQLExtensionDeclaration = api => {
// For schema
api.loaders.schema.register(new CodeFileLoader());

api.loaders.schema.register(
new CodeFileLoader({
noSilentErrors: false,
noRequire: false,
noPluck: false,
// // @ts-expect-error
// require: true,
}),
);
// For documents
api.loaders.documents.register(new CodeFileLoader());
api.loaders.documents.register(
new CodeFileLoader({
noSilentErrors: false,
noRequire: false,
noPluck: false,
// // @ts-expect-error
// require: true,
}),
);

return { name: 'languageService' };
};
Expand Down Expand Up @@ -137,23 +154,16 @@ export class GraphQLCache implements GraphQLCacheInterface {
};

getFragmentDependencies = async (
query: string,
documentAST: DocumentNode,
fragmentDefinitions?: Map<string, FragmentInfo> | null,
): Promise<FragmentInfo[]> => {
// If there isn't context for fragment references,
// return an empty array.
if (!fragmentDefinitions) {
return [];
}
// If the query cannot be parsed, validations cannot happen yet.
// Return an empty array.
let parsedQuery;
try {
parsedQuery = parse(query);
} catch {
return [];
}
return this.getFragmentDependenciesForAST(parsedQuery, fragmentDefinitions);

return this.getFragmentDependenciesForAST(documentAST, fragmentDefinitions);
};

getFragmentDependenciesForAST = async (
Expand Down Expand Up @@ -217,12 +227,16 @@ export class GraphQLCache implements GraphQLCacheInterface {
// This function may be called from other classes.
// If then, check the cache first.
const rootDir = projectConfig.dirpath;
console.log('getFragmentDefinitions 0');
const cacheKey = this._cacheKeyForProject(projectConfig);
console.log('getFragmentDefinitions .5');
if (this._fragmentDefinitionsCache.has(cacheKey)) {
console.log('getFragmentDefinitions cache hit');
return this._fragmentDefinitionsCache.get(cacheKey) || new Map();
}

console.log('getFragmentDefinitions 1');
const list = await this._readFilesFromInputDirs(rootDir, projectConfig);
console.log('getFragmentDefinitions 2');

const { fragmentDefinitions, graphQLFileMap } =
await this.readAllGraphQLFiles(list);
Expand Down Expand Up @@ -345,8 +359,10 @@ export class GraphQLCache implements GraphQLCacheInterface {
rootDir: string,
projectConfig: GraphQLProjectConfig,
): Promise<Array<GraphQLFileMetadata>> => {
return Promise.resolve([])
let pattern: string;
const patterns = this._getSchemaAndDocumentFilePatterns(projectConfig);
console.log('patterns', patterns);

// See https://github.com/graphql/graphql-language-service/issues/221
// for details on why special handling is required here for the
Expand All @@ -359,49 +375,64 @@ export class GraphQLCache implements GraphQLCacheInterface {
}

return new Promise((resolve, reject) => {
const globResult = new glob.Glob(
pattern,
{
cwd: rootDir,
stat: true,
absolute: false,
ignore: [
'generated/relay',
'**/__flow__/**',
'**/__generated__/**',
'**/__github__/**',
'**/__mocks__/**',
'**/node_modules/**',
'**/__flowtests__/**',
],
},
error => {
if (error) {
reject(error);
}
},
);
globResult.on('end', () => {
resolve(
Object.keys(globResult.statCache)
.filter(
filePath => typeof globResult.statCache[filePath] === 'object',
)
.filter(filePath => projectConfig.match(filePath))
.map(filePath => {
// @TODO
// so we have to force this here
// because glob's DefinitelyTyped 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: URI.file(filePath).toString(),
mtime: Math.trunc(cacheEntry.mtime.getTime() / 1000),
size: cacheEntry.size,
};
}),
console.log('about to glob', glob.Glob);
try {
const globResult = new glob.Glob(
pattern,
{
cwd: rootDir,
stat: true,
absolute: false,
strict: false,
ignore: [
'generated/relay',
'**/__flow__/**',
'**/__generated__/**',
'**/__github__/**',
'**/__mocks__/**',
'**/node_modules/**',
'**/__flowtests__/**',
],
},
error => {
if (error) {
console.log('error');
reject(error);
}
console.log('empty error');
},
);
});
globResult.on('error', error => {
console.log(error);
reject(error);
});
globResult.on('end', () => {
console.log('end');
resolve(
Object.keys(globResult.statCache)
.filter(
filePath => typeof globResult.statCache[filePath] === 'object',
)
.filter(filePath => projectConfig.match(filePath))
.map(filePath => {
// @TODO
// so we have to force this here
// because glob's DefinitelyTyped 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: URI.file(filePath).toString(),
mtime: Math.trunc(cacheEntry.mtime.getTime() / 1000),
size: cacheEntry.size,
};
}),
);
});
} catch (error) {
console.log(error);

reject(error);
}
});
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,26 @@ export class GraphQLLanguageService {
// Perform syntax diagnostics first, as this doesn't require
// schema/fragment definitions, even the project configuration.
let documentHasExtensions = false;
let documentAST: DocumentNode;
console.log('get project');
const projectConfig = this.getConfigForURI(uri);
console.log('project');

// skip validation when there's nothing to validate, prevents noisy unexpected EOF errors
if (!projectConfig || !document || document.trim().length < 2) {
console.log('validate 1.5');
return [];
}
const { schema: schemaPath, name: projectName, extensions } = projectConfig;
const { name: projectName, extensions } = projectConfig;

console.log('validate 1');
const fragmentDefinitions = await this._graphQLCache.getFragmentDefinitions(
projectConfig,
);
console.log('validate 2');

try {
const documentAST = parse(document);
if (!schemaPath || uri !== schemaPath) {
documentAST = parse(document);
documentHasExtensions = documentAST.definitions.some(definition => {
switch (definition.kind) {
case Kind.OBJECT_TYPE_DEFINITION:
Expand All @@ -141,8 +151,9 @@ export class GraphQLLanguageService {

return false;
});
}
console.log('validate 3');
} catch (error) {
console.log(error)
if (error instanceof GraphQLError) {
const range = getRange(
error.locations?.[0] ?? { column: 0, line: 0 },
Expand All @@ -163,16 +174,13 @@ export class GraphQLLanguageService {

// If there's a matching config, proceed to prepare to run validation
let source = document;
const fragmentDefinitions = await this._graphQLCache.getFragmentDefinitions(
projectConfig,
);

const fragmentDependencies =
await this._graphQLCache.getFragmentDependencies(
document,
documentAST,
fragmentDefinitions,
);

console.log('validate 4');
const dependenciesSource = fragmentDependencies.reduce(
(prev, cur) => `${prev} ${print(cur.definition)}`,
'',
Expand All @@ -190,7 +198,7 @@ export class GraphQLLanguageService {
// query, so we return an empty array here.
return [];
}

console.log('validate 5');
// Check if there are custom validation rules to be used
let customRules: ValidationRule[] | null = null;
if (
Expand All @@ -201,11 +209,12 @@ export class GraphQLLanguageService {

/* eslint-enable no-implicit-coercion */
}
console.log('validate 6');
const schema = await this._graphQLCache.getSchema(
projectName,
documentHasExtensions,
);

console.log('validate 7');
if (!schema) {
return [];
}
Expand Down
Loading