Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix incorrectly using 0-indexed number where 1-indexed number is expected #3397

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
5 changes: 5 additions & 0 deletions .changeset/little-hats-explain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'monaco-graphql': patch
---

Fixes an off-by-one bug that caused console spam when hovering over things in the first line of a graphql file with monaco, because `toGraphQLPosition()` from `monaco-graphql` returns 0-based line/col numbers and `getRange()` from `graphql-language-service` expects 1-based line/col numbers.
2 changes: 0 additions & 2 deletions .github/workflows/main-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,3 @@ jobs:
files: coverage/lcov.info
fail_ci_if_error: true
verbose: true


2 changes: 2 additions & 0 deletions jest.config.base.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ module.exports = (dir, env = 'jsdom') => {
'^cm6-graphql\\/src\\/([^]+)': `${__dirname}/packages/cm6-graphql/dist/$1`,
'^example-([^/]+)': `${__dirname}/examples/$1/src`,
'^-!svg-react-loader.*$': '<rootDir>/resources/jest/svgImportMock.js',
// because of the svelte compiler's export patterns i guess?
'svelte/compiler': `${__dirname}/node_modules/svelte/compiler.cjs`,
},
testMatch: ['**/*[-.](spec|test).[jt]s?(x)', '!**/cypress/**'],
testEnvironment: env,
Expand Down
9 changes: 5 additions & 4 deletions packages/graphql-language-service-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,13 @@
"mkdirp": "^1.0.4",
"node-abort-controller": "^3.0.1",
"nullthrows": "^1.0.0",
"svelte": "^4.0.0",
"svelte2tsx": "^0.6.16",
"vscode-jsonrpc": "^8.0.1",
"vscode-languageserver": "^8.0.1",
"vscode-languageserver-types": "^3.17.1",
"vscode-uri": "^3.0.2"
"vscode-languageserver-types": "^3.17.2",
"vscode-uri": "^3.0.2",
"svelte2tsx": "^0.6.19",
"svelte": "^4.1.1",
"source-map-js": "1.0.2"
},
"devDependencies": {
"@types/glob": "^8.1.0",
Expand Down
20 changes: 15 additions & 5 deletions packages/graphql-language-service-server/src/GraphQLCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import type {
ObjectTypeInfo,
Uri,
} from 'graphql-language-service';
import type { Logger } from 'vscode-languageserver';

import * as fs from 'node:fs';
import { readFile } from 'node:fs/promises';
Expand All @@ -48,6 +47,11 @@ import glob from 'glob';
import { LoadConfigOptions } from './types';
import { URI } from 'vscode-uri';
import { CodeFileLoader } from '@graphql-tools/code-file-loader';
import {
DEFAULT_SUPPORTED_EXTENSIONS,
DEFAULT_SUPPORTED_GRAPHQL_EXTENSIONS,
} from './constants';
import { NoopLogger, Logger } from './Logger';

const LanguageServiceExtension: GraphQLExtensionDeclaration = api => {
// For schema
Expand All @@ -68,7 +72,7 @@ export async function getGraphQLCache({
config,
}: {
parser: typeof parseDocument;
logger: Logger;
logger: Logger | NoopLogger;
loadConfigOptions: LoadConfigOptions;
config?: GraphQLConfig;
}): Promise<GraphQLCache> {
Expand Down Expand Up @@ -98,7 +102,7 @@ export class GraphQLCache implements GraphQLCacheInterface {
_fragmentDefinitionsCache: Map<Uri, Map<string, FragmentInfo>>;
_typeDefinitionsCache: Map<Uri, Map<string, ObjectTypeInfo>>;
_parser: typeof parseDocument;
_logger: Logger;
_logger: Logger | NoopLogger;

constructor({
configDir,
Expand All @@ -109,7 +113,7 @@ export class GraphQLCache implements GraphQLCacheInterface {
configDir: Uri;
config: GraphQLConfig;
parser: typeof parseDocument;
logger: Logger;
logger: Logger | NoopLogger;
}) {
this._configDir = configDir;
this._graphQLConfig = config;
Expand Down Expand Up @@ -827,7 +831,13 @@ export class GraphQLCache implements GraphQLCacheInterface {
let queries: CachedContent[] = [];
if (content.trim().length !== 0) {
try {
queries = this._parser(content, filePath);
queries = this._parser(
content,
filePath,
DEFAULT_SUPPORTED_EXTENSIONS,
DEFAULT_SUPPORTED_GRAPHQL_EXTENSIONS,
this._logger,
);
if (queries.length === 0) {
// still resolve with an empty ast
return {
Expand Down
16 changes: 10 additions & 6 deletions packages/graphql-language-service-server/src/MessageProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,12 @@ import type {
WorkspaceSymbolParams,
Connection,
DidChangeConfigurationRegistrationOptions,
Logger,
} from 'vscode-languageserver/node';

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

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

import { printSchema, visit, parse, FragmentDefinitionNode } from 'graphql';
import { tmpdir } from 'node:os';
Expand All @@ -72,6 +71,11 @@ import {
ProjectNotFoundError,
} from 'graphql-config';
import type { LoadConfigOptions } from './types';
import {
DEFAULT_SUPPORTED_EXTENSIONS,
SupportedExtensionsEnum,
} from './constants';
import { NoopLogger, Logger } from './Logger';

const configDocLink =
'https://www.npmjs.com/package/graphql-language-service-server#user-content-graphql-configuration-file';
Expand All @@ -93,7 +97,7 @@ export class MessageProcessor {
_isInitialized = false;
_isGraphQLConfigMissing: boolean | null = null;
_willShutdown = false;
_logger: Logger;
_logger: Logger | NoopLogger;
_extensions?: GraphQLExtensionDeclaration[];
_parser: (text: string, uri: string) => CachedContent[];
_tmpDir: string;
Expand All @@ -114,8 +118,8 @@ export class MessageProcessor {
tmpDir,
connection,
}: {
logger: Logger;
fileExtensions: string[];
logger: Logger | NoopLogger;
fileExtensions: ReadonlyArray<SupportedExtensionsEnum>;
graphqlFileExtensions: string[];
loadConfigOptions: LoadConfigOptions;
config?: GraphQLConfig;
Expand Down Expand Up @@ -788,7 +792,7 @@ export class MessageProcessor {
if (parentRange && res.name) {
const isInline = inlineFragments.includes(res.name);
const isEmbedded = DEFAULT_SUPPORTED_EXTENSIONS.includes(
path.extname(textDocument.uri),
path.extname(textDocument.uri) as SupportedExtensionsEnum,
);
if (isInline && isEmbedded) {
const vOffset = parentRange.start.line;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ describe('MessageProcessor', () => {
// @ts-ignore
connection: {},
logger,
fileExtensions: ['js'],
graphqlFileExtensions: ['graphql'],
loadConfigOptions: { rootDir: __dirname },
});
Expand All @@ -56,6 +55,7 @@ describe('MessageProcessor', () => {
configDir: __dirname,
config: gqlConfig,
parser: parseDocument,
logger: new NoopLogger(),
});
messageProcessor._languageService = {
// @ts-ignore
Expand Down Expand Up @@ -484,6 +484,7 @@ export function Example(arg: string) {
}`;

const contents = parseDocument(text, 'test.tsx');

expect(contents[0].query).toEqual(`
query Test {
test {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@
*
*/

import { Position, Range } from 'graphql-language-service';
import { findGraphQLTags as baseFindGraphQLTags } from '../findGraphQLTags';

jest.mock('../Logger');

import { NoopLogger } from '../Logger';
import { SupportedExtensionsEnum } from '../constants';

describe('findGraphQLTags', () => {
const logger = new NoopLogger();
const findGraphQLTags = (text: string, ext: string) =>
const findGraphQLTags = (text: string, ext: SupportedExtensionsEnum) =>
baseFindGraphQLTags(text, ext, '', logger);

it('finds queries in tagged templates', async () => {
Expand Down Expand Up @@ -315,15 +317,46 @@ query {id}`);

it('finds queries in tagged templates in Svelte using normal <script>', async () => {
const text = `
<script>
gql\`
query {id}
\`;
<script context="module">
const query = graphql(\`
query AllCharacters {
characters {
results {
name
id
image
}
}
}
\`)
export async function load({fetch}) {
return {
props: {
_data: await fetch({
text: query
})
}
}
}

</script>
`;
const contents = findGraphQLTags(text, '.svelte');
expect(contents[0].template).toEqual(`
query {id}`);
query AllCharacters {
characters {
results {
name
id
image
}
}
}
`);

expect(JSON.stringify(contents[0].range)).toEqual(
JSON.stringify(new Range(new Position(2, 29), new Position(12, 0))),
);
});

it('no crash in Svelte files without <script>', async () => {
Expand Down
100 changes: 100 additions & 0 deletions packages/graphql-language-service-server/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import type { ParserOptions, ParserPlugin } from '@babel/parser';
// Attempt to be as inclusive as possible of source text.
export const PARSER_OPTIONS: ParserOptions = {
allowImportExportEverywhere: true,
allowReturnOutsideFunction: true,
allowSuperOutsideMethod: true,
allowAwaitOutsideFunction: true,
// important! this allows babel to keep parsing when there are issues
errorRecovery: true,
sourceType: 'module',
strictMode: false,
};

/**
* .graphql is the officially recommended extension for graphql files
*
* .gql and .graphqls are included for compatibility for commonly used extensions
*
* GQL is a registered trademark of Google, and refers to Google Query Language.
* GraphQL Foundation does *not* recommend using this extension or acronym for
* referring to GraphQL.
*
* any changes should also be reflected in vscode-graphql-syntax textmate grammar & package.json
*/
export const DEFAULT_SUPPORTED_GRAPHQL_EXTENSIONS = [
'.graphql',
'.graphqls',
'.gql',
];

/**
* default tag delimiters to use when parsing GraphQL strings (for js/ts/vue/svelte)
* any changes should also be reflected in vscode-graphql-syntax textmate grammar
*/
export const TAG_MAP: Record<string, true> = {
graphql: true,
gql: true,
graphqls: true,
};

/**
* default extensions to use when parsing for GraphQL strings
* any changes should also be reflected in vscode-graphql-syntax textmate grammar & package.json
*/
export const DEFAULT_SUPPORTED_EXTENSIONS = [
'.js',
'.cjs',
'.mjs',
'.es',
'.esm',
'.es6',
'.ts',
'.jsx',
'.tsx',
'.vue',
'.svelte',
'.cts',
'.mts',
] as const;
export type SupportedExtensions = typeof DEFAULT_SUPPORTED_EXTENSIONS;
export type SupportedExtensionsEnum =
(typeof DEFAULT_SUPPORTED_EXTENSIONS)[number];

/**
* default plugins to use with babel parser
*/
export const BABEL_PLUGINS: ParserPlugin[] = [
'asyncDoExpressions',
'asyncGenerators',
'bigInt',
'classProperties',
'classPrivateProperties',
'classPrivateMethods',
'classStaticBlock',
'doExpressions',
'decimal',
'decorators-legacy',
'destructuringPrivate',
'dynamicImport',
'exportDefaultFrom',
'exportNamespaceFrom',
'functionBind',
'functionSent',
'importMeta',
'importAssertions',
'jsx',
'logicalAssignment',
'moduleBlocks',
'moduleStringNames',
'nullishCoalescingOperator',
'numericSeparator',
'objectRestSpread',
'optionalCatchBinding',
'optionalChaining',
// ['pipelineOperator', { proposal: 'hack' }],
'privateIn',
'regexpUnicodeSets',
'throwExpressions',
'topLevelAwait',
];
Loading