Mirror: TypeScript LSP plugin that finds GraphQL documents in your code and provides diagnostics, auto-complete and hover-information.

chore: resolve schema from alternative rootDirectories (#257)

Changed files
+74 -7
.changeset
packages
graphqlsp
+5
.changeset/cool-elephants-invent.md
···
+
---
+
'@0no-co/graphqlsp': patch
+
---
+
+
Add support for alternative root directories, when your tsconfig does not define GraphQLSP we'll traverse up until we find the `extends` that does and resolve the schema from there
+2
packages/graphqlsp/package.json
···
"graphql": "^16.8.1",
"graphql-language-service": "^5.2.0",
"lru-cache": "^10.0.1",
+
"type-fest": "^4.11.1",
"typescript": "^5.3.3"
},
"dependencies": {
+
"json5": "^2.2.3",
"node-fetch": "^2.0.0"
},
"publishConfig": {
+33 -5
packages/graphqlsp/src/graphql/getSchema.ts
···
IntrospectionQuery,
introspectionFromSchema,
} from 'graphql';
-
+
import path from 'path';
+
import JSON5 from 'json5';
import { minifyIntrospectionQuery } from '@urql/introspection';
import fetch from 'node-fetch';
-
import path from 'path';
import fs from 'fs';
+
import type { TsConfigJson } from 'type-fest';
+
import { ts } from '../ts';
import { Logger } from '../index';
const preambleComments =
···
const json = JSON.stringify(minified, null, 2);
-
let output = path.resolve(path.dirname(root), tadaOutputLocation);
+
let output = path.resolve(root, tadaOutputLocation);
let stat: fs.Stats | undefined;
let contents = '';
···
headers: Record<string, unknown>;
};
+
const getRootDir = (
+
info: ts.server.PluginCreateInfo,
+
tsconfigPath: string
+
): string | undefined => {
+
const tsconfigContents = info.project.readFile(tsconfigPath);
+
const parsed = JSON5.parse<TsConfigJson>(tsconfigContents!);
+
+
if (
+
parsed.compilerOptions?.plugins?.find(x => x.name === '@0no-co/graphqlsp')
+
) {
+
return path.dirname(tsconfigPath);
+
} else if (Array.isArray(parsed.extends)) {
+
return parsed.extends.find(p => {
+
const resolved = path.resolve(path.dirname(tsconfigPath), p);
+
return getRootDir(info, resolved);
+
});
+
} else if (parsed.extends) {
+
const resolved = path.resolve(path.dirname(tsconfigPath), parsed.extends);
+
return getRootDir(info, resolved);
+
}
+
};
+
export const loadSchema = (
-
root: string,
+
info: ts.server.PluginCreateInfo,
schema: SchemaOrigin | string,
tadaOutputLocation: string | undefined,
logger: Logger
): { current: GraphQLSchema | null; version: number } => {
+
const root =
+
getRootDir(info, info.project.getProjectName()) ||
+
path.dirname(info.project.getProjectName());
+
logger('Got root-directory to resolve schema from: ' + root);
const ref: {
current: GraphQLSchema | null;
version: number;
···
pollSchema();
} else if (typeof schema === 'string') {
const isJson = path.extname(schema) === '.json';
-
const resolvedPath = path.resolve(path.dirname(root), schema);
+
const resolvedPath = path.resolve(root, schema);
logger(`Getting schema from ${resolvedPath}`);
async function readSchema() {
+1 -1
packages/graphqlsp/src/index.ts
···
const proxy = createBasicDecorator(info);
const schema = loadSchema(
-
info.project.getProjectName(),
+
info,
config.schema,
// TODO: either we check here for the client having a package.json
// with gql.tada and use a default file loc or we use a config
+12 -1
pnpm-lock.yaml
···
packages/graphqlsp:
dependencies:
+
json5:
+
specifier: ^2.2.3
+
version: 2.2.3
node-fetch:
specifier: ^2.0.0
version: 2.6.7
···
lru-cache:
specifier: ^10.0.1
version: 10.0.1
+
type-fest:
+
specifier: ^4.11.1
+
version: 4.11.1
typescript:
specifier: ^5.3.3
version: 5.3.3
···
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
engines: {node: '>=6'}
hasBin: true
-
dev: true
/jsonc-parser@3.2.0:
resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
···
engines: {node: '>=10'}
dev: true
+
/type-fest@4.11.1:
+
resolution: {integrity: sha512-MFMf6VkEVZAETidGGSYW2B1MjXbGX+sWIywn2QPEaJ3j08V+MwVRHMXtf2noB8ENJaD0LIun9wh5Z6OPNf1QzQ==}
+
engines: {node: '>=16'}
+
dev: true
+
/typed-array-length@1.0.4:
resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==}
dependencies:
···
resolution: {directory: packages/graphqlsp, type: directory}
name: '@0no-co/graphqlsp'
dependencies:
+
json5: 2.2.3
node-fetch: 2.6.7
transitivePeerDependencies:
- encoding
+21
tsconfig.json
···
+
{
+
"compilerOptions": {
+
"plugins": [
+
{
+
"name": "@0no-co/graphqlsp",
+
"schema": "./packages/example-tada/schema.graphql",
+
"tadaOutputLocation": "./packages/example-tada/introspection.ts"
+
}
+
],
+
"jsx": "react-jsx",
+
/* Language and Environment */
+
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
+
/* Modules */
+
"module": "commonjs" /* Specify what module code is generated. */,
+
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
+
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
+
/* Type Checking */
+
"strict": true /* Enable all strict type-checking options. */,
+
"skipLibCheck": true /* Skip type checking all .d.ts files. */
+
}
+
}