Skip to content

Commit

Permalink
Derive extensions for supported languages and monitored files from ot…
Browse files Browse the repository at this point in the history
…her installed extensions. (#184)

Co-authored-by: Jerel Miller <[email protected]>
  • Loading branch information
phryneas and jerelmiller authored Aug 28, 2024
1 parent 28f144f commit 9c53a11
Show file tree
Hide file tree
Showing 15 changed files with 292 additions and 113 deletions.
13 changes: 13 additions & 0 deletions .changeset/clever-gifts-scream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"vscode-apollo": minor
---

Derive extensions for supported languages and monitored files from other installed extensions.
Adjust default `includes` for client projects.

This changes the default `includes` similar to (depending on additional extensions you might have installed):

```diff
-'src/**/*.{ts,tsx,js,jsx,graphql,gql}',
+'src/**/*{.gql,.graphql,.graphqls,.js,.mjs,.cjs,.es6,.pac,.ts,.mts,.cts,.jsx,.tsx,.vue,.svelte,.py,.rpy,.pyw,.cpy,.gyp,.gypi,.pyi,.ipy,.pyt,.rb,.rbx,.rjs,.gemspec,.rake,.ru,.erb,.podspec,.rbi,.dart,.re,.ex,.exs}'
```
1 change: 1 addition & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"env": {
"APOLLO_ENGINE_ENDPOINT": "http://localhost:7096/apollo",
"APOLLO_FEATURE_FLAGS": "rover"
//"APOLLO_ROVER_LANGUAGE_IDS": "graphql,javascript"
},
"outFiles": ["${workspaceRoot}/lib/**/*.js"]
},
Expand Down
14 changes: 8 additions & 6 deletions src/language-server/config/__tests__/loadConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,6 @@ Object {
"**/node_modules",
"**/__tests__",
],
"includes": Array [
"src/**/*.{ts,tsx,js,jsx,graphql,gql}",
],
"service": "hello",
"tagName": "gql",
},
Expand Down Expand Up @@ -395,9 +392,14 @@ Object {
configPath: dirPath,
});

expect((config?.rawConfig as any).client.includes).toEqual([
"src/**/*.{ts,tsx,js,jsx,graphql,gql}",
]);
expect((config?.rawConfig as any).client.includes).toEqual(
/**
* This will be calculated in the `GraphQLInternalProject` constructor by calling `getSupportedExtensions()`
* which will have information about all the extensions added by other VSCode extensions for the language ids
* that Apollo supports.
*/
undefined,
);
});

it("merges engine config defaults", async () => {
Expand Down
4 changes: 1 addition & 3 deletions src/language-server/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ const clientConfig = z.object({
])
.optional(),
// maybe shared with rover?
includes: z
.array(z.string())
.default(["src/**/*.{ts,tsx,js,jsx,graphql,gql}"]),
includes: z.array(z.string()).optional(),
// maybe shared with rover?
excludes: z.array(z.string()).default(["**/node_modules", "**/__tests__"]),
// maybe shared with rover?
Expand Down
21 changes: 8 additions & 13 deletions src/language-server/fileSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@ export class FileSet {
rootURI,
includes,
excludes,
configURI,
}: {
rootURI: URI;
includes: string[];
excludes: string[];
configURI?: URI;
}) {
invariant(rootURI, `Must provide "rootURI".`);
invariant(includes, `Must provide "includes".`);
Expand All @@ -30,13 +28,6 @@ export class FileSet {
this.excludes = excludes;
}

pushIncludes(files: string[]) {
this.includes.push(...files);
}
pushExcludes(files: string[]) {
this.excludes.push(...files);
}

includesFile(filePath: string): boolean {
const normalizedFilePath = normalizeURI(filePath);

Expand All @@ -57,10 +48,14 @@ export class FileSet {
}

allFiles(): string[] {
// since glob.sync takes a single pattern, but we allow an array of `includes`, we can join all the
// `includes` globs into a single pattern and pass to glob.sync. The `ignore` option does, however, allow
// an array of globs to ignore, so we can pass it in directly
const joinedIncludes = `{${this.includes.join(",")}}`;
const joinedIncludes =
this.includes.length == 1
? this.includes[0]
: // since glob.sync takes a single pattern, but we allow an array of `includes`, we can join all the
// `includes` globs into a single pattern and pass to glob.sync. The `ignore` option does, however, allow
// an array of globs to ignore, so we can pass it in directly
`{${this.includes.join(",")}}`;

return globSync(joinedIncludes, {
cwd: this.rootURI.fsPath,
absolute: true,
Expand Down
11 changes: 7 additions & 4 deletions src/language-server/project/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,13 @@ export abstract class GraphQLProject {
private readyPromise: Promise<void>;
public config: ApolloConfig;
protected schema?: GraphQLSchema;
protected fileSet: FileSet;
protected rootURI: URI;
protected loadingHandler: LoadingHandler;

protected lastLoadDate?: number;

private configFileSet: FileSet;

constructor({
config,
configFolderURI,
Expand All @@ -63,7 +64,7 @@ export abstract class GraphQLProject {
// if a config doesn't have a uri associated, we can assume the `rootURI` is the project's root.
this.rootURI = config.configDirURI || configFolderURI;

this.fileSet = new FileSet({
this.configFileSet = new FileSet({
rootURI: this.rootURI,
includes: [
".env",
Expand All @@ -73,7 +74,6 @@ export abstract class GraphQLProject {
"apollo.config.ts",
],
excludes: [],
configURI: config.configURI,
});

this._isReady = false;
Expand Down Expand Up @@ -117,7 +117,10 @@ export abstract class GraphQLProject {
this._onDiagnostics = handler;
}

abstract includesFile(uri: DocumentUri): boolean;
abstract includesFile(uri: DocumentUri, languageId?: string): boolean;
isConfiguredBy(uri: DocumentUri): boolean {
return this.configFileSet.includesFile(uri);
}

abstract onDidChangeWatchedFiles: ConnectionHandler["onDidChangeWatchedFiles"];
abstract onDidOpen?: (event: TextDocumentChangeEvent<TextDocument>) => void;
Expand Down
15 changes: 1 addition & 14 deletions src/language-server/project/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,20 +201,7 @@ export class GraphQLClientProject extends GraphQLInternalProject {
super({ config, configFolderURI, loadingHandler, clientIdentity });
this.serviceID = config.graph;

/**
* This function is used in the Array.filter function below it to remove any .env files and config files.
* If there are 0 files remaining after removing those files, we should warn the user that their config
* may be wrong. We shouldn't throw an error here, since they could just be initially setting up a project
* and there's no way to know for sure that there _should_ be files.
*/
const filterConfigAndEnvFiles = (path: string) =>
!(
path.includes("apollo.config") ||
path.includes(".env") ||
(config.configURI && path === config.configURI.fsPath)
);

if (this.allIncludedFiles().filter(filterConfigAndEnvFiles).length === 0) {
if (this.allIncludedFiles().length === 0) {
console.warn(
"⚠️ It looks like there are 0 files associated with this Apollo Project. " +
"This may be because you don't have any files yet, or your includes/excludes " +
Expand Down
26 changes: 18 additions & 8 deletions src/language-server/project/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import {
import { ApolloEngineClient, ClientIdentity } from "../engine";
import { GraphQLProject, DocumentUri, GraphQLProjectConfig } from "./base";
import throttle from "lodash.throttle";
import { FileSet } from "../fileSet";
import { getSupportedExtensions } from "../utilities/languageIdForExtension";

const fileAssociations: { [extension: string]: string } = {
".graphql": "graphql",
Expand Down Expand Up @@ -58,6 +60,7 @@ export abstract class GraphQLInternalProject
{
public schemaProvider: GraphQLSchemaProvider;
protected engineClient?: ApolloEngineClient;
private fileSet: FileSet;

private needsValidation = false;

Expand All @@ -70,16 +73,23 @@ export abstract class GraphQLInternalProject
clientIdentity,
}: GraphQLInternalProjectConfig) {
super({ config, configFolderURI, loadingHandler });
const { includes = [], excludes = [] } = config.client;
const {
// something like
// 'src/**/*{.gql,.graphql,.graphqls,.js,.mjs,.cjs,.es6,.pac,.ts,.mts,.cts,.jsx,.tsx,.vue,.svelte,.py,.rpy,.pyw,.cpy,.gyp,.gypi,.pyi,.ipy,.pyt,.rb,.rbx,.rjs,.gemspec,.rake,.ru,.erb,.podspec,.rbi,.dart,.re,.ex,.exs}'
includes = [`src/**/*{${getSupportedExtensions().join(",")}}`],
excludes = [],
} = config.client;

this.documentsByFile = new Map();

this.fileSet.pushIncludes(includes);
// We do not want to include the local schema file in our list of documents
this.fileSet.pushExcludes([
...excludes,
...this.getRelativeLocalSchemaFilePaths(),
]);
this.fileSet = new FileSet({
rootURI: this.rootURI,
includes,
excludes: [
...excludes,
// We do not want to include the local schema file in our list of documents
...this.getRelativeLocalSchemaFilePaths(),
],
});

this.schemaProvider = schemaProviderFromConfig(config, clientIdentity);
const { engine } = config;
Expand Down
17 changes: 15 additions & 2 deletions src/language-server/project/rover/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ import { ApolloConfig, RoverConfig } from "../../config";
import { DocumentSynchronization } from "./DocumentSynchronization";
import { AsyncLocalStorage } from "node:async_hooks";
import internal from "node:stream";
import { getLanguageIdForExtension } from "../../utilities/languageIdForExtension";
import { extname } from "node:path";
import type { FileExtension } from "../../../tools/utilities/languageInformation";

export const DEBUG = true;

Expand All @@ -38,6 +41,10 @@ export interface RoverProjectConfig extends GraphQLProjectConfig {
capabilities: ClientCapabilities;
}

const supportedLanguageIds = (
process.env.APOLLO_ROVER_LANGUAGE_IDS || "graphql"
).split(",");

export class RoverProject extends GraphQLProject {
config: RoverConfig;
/**
Expand Down Expand Up @@ -203,8 +210,14 @@ export class RoverProject extends GraphQLProject {
return { type: "Rover", loaded: true };
}

includesFile(uri: DocumentUri) {
return uri.startsWith(this.rootURI.toString());
includesFile(
uri: DocumentUri,
languageId = getLanguageIdForExtension(extname(uri) as FileExtension),
) {
return (
uri.startsWith(this.rootURI.toString()) &&
supportedLanguageIds.includes(languageId)
);
}

validate?: () => void;
Expand Down
Loading

0 comments on commit 9c53a11

Please sign in to comment.