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

[AT-10] Add helpful errors / messaging around config files #897

Merged
merged 14 commits into from
Jan 30, 2019
Merged
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
- Improve client:check output [#934](https://github.com/apollographql/apollo-tooling/pull/934)
- `apollo-language-server`
- Replace checkOperations mutation with new validateOperations mutation [#934](https://github.com/apollographql/apollo-tooling/pull/934)
- Include config files into a project's fileSet [#897](https://github.com/apollographql/apollo-tooling/pull/897)
- Add hook into workspace for communicating out when configs are loaded or when errors are found [#897](https://github.com/apollographql/apollo-tooling/pull/897)
- Add fn to workspace for reloading a project with a given config URI [#897](https://github.com/apollographql/apollo-tooling/pull/897)
- Reload project when config file is changed [#897](https://github.com/apollographql/apollo-tooling/pull/897)
- Update error handling from within the server (send as message). This message can be listened for and handled by the consumer [#897](https://github.com/apollographql/apollo-tooling/pull/897)
- `vscode-apollo`
- Update statusBar to reflect new possible "warning" states [#897](https://github.com/apollographql/apollo-tooling/pull/897)

## `[email protected]`

Expand Down
30 changes: 20 additions & 10 deletions packages/apollo-language-server/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import TypeScriptLoader from "@endemolshinegroup/cosmiconfig-typescript-loader";
import { resolve, dirname } from "path";
import { readFileSync, existsSync } from "fs";
import { merge } from "lodash/fp";

import {
ServiceID,
ServiceSpecifier,
Expand Down Expand Up @@ -139,6 +138,8 @@ export interface LoadConfigSettings {
// config loading only works on node so we default to
// process.cwd()
configPath?: string;
configFileName?: string;
requireConfig?: boolean;
name?: string;
type?: "service" | "client";
}
Expand Down Expand Up @@ -273,18 +274,27 @@ const getServiceFromKey = (key: string | undefined): string | undefined => {
// XXX load .env files automatically
export const loadConfig = async ({
configPath,
configFileName,
requireConfig = false,
name,
type
}: LoadConfigSettings): Promise<ApolloConfig> => {
const explorer = cosmiconfig(MODULE_NAME, {
searchPlaces: getSearchPlaces(configPath),
searchPlaces: getSearchPlaces(configFileName),
loaders
});

let loadedConfig = (await explorer.search(configPath)) as ConfigResult<
ApolloConfigFormat
>;

if (requireConfig && !loadedConfig) {
throw new Error(
`No Apollo config found for project. For more information, please refer to:
https://bit.ly/2ByILPj`
);
}

// add API to the env
let engineConfig = {},
nameFromKey;
Expand Down Expand Up @@ -328,7 +338,7 @@ export const loadConfig = async ({
resolvedType = "service";
} else {
throw new Error(
"Unable to resolve project type. Please add either a client or service config. For more information, please refer to https://www.apollographql.com/docs/references/apollo-config.html"
"Unable to resolve project type. Please add either a client or service config. For more information, please refer to https://bit.ly/2ByILPj"
);
}

Expand All @@ -339,24 +349,24 @@ export const loadConfig = async ({
loadedConfig = {
isEmpty: false,
filepath: configPath || process.cwd(),
config:
resolvedType === "client"
config: {
...(loadedConfig && loadedConfig.config),
...(resolvedType === "client"
? {
...(loadedConfig ? loadedConfig.config : {}),
client: {
...DefaultConfigBase,
...(loadedConfig ? loadedConfig.config.client : {}),
...(loadedConfig && loadedConfig.config.client),
service: resolvedName
}
}
: {
...(loadedConfig ? loadedConfig.config : {}),
service: {
...DefaultConfigBase,
...(loadedConfig ? loadedConfig.config.service : {}),
...(loadedConfig && loadedConfig.config.service),
name: resolvedName
}
}
})
}
};
}

Expand Down
5 changes: 5 additions & 0 deletions packages/apollo-language-server/src/project/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ export abstract class GraphQLProject implements GraphQLSchemaProvider {
return this.readyPromise;
}

public updateConfig(config: ApolloConfig) {
this.config = config;
return this.initialize();
}

public resolveSchema(config: SchemaResolveConfig): Promise<GraphQLSchema> {
this.lastLoadDate = +new Date();
return this.schemaProvider.resolveSchema(config);
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-language-server/src/project/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export class GraphQLClientProject extends GraphQLProject {
// the URI of the folder _containing_ the apollo.config.js is the true project's root.
// if a config doesn't have a uri associated, we can assume the `rootURI` is the project's root.
rootURI: config.configDirURI || rootURI,
includes: config.client.includes,
includes: [...config.client.includes, ".env", "apollo.config.js"],
excludes: config.client.excludes
});

Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-language-server/src/project/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class GraphQLServiceProject extends GraphQLProject {
}: GraphQLServiceProjectConfig) {
const fileSet = new FileSet({
rootURI: config.configDirURI || rootURI,
includes: config.service.includes,
includes: [...config.service.includes, ".env", "apollo.config.js"],
excludes: config.service.excludes
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,7 @@ export function schemaProviderFromConfig(
}
}

throw new Error("No provider was created for config");
throw new Error(
"No schema provider was created, because the project type was unable to be resolved from your config. Please add either a client or service config. For more information, please refer to https://bit.ly/2ByILPj"
);
}
69 changes: 32 additions & 37 deletions packages/apollo-language-server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
ProposedFeatures,
TextDocuments,
FileChangeType,
ServerCapabilities
ServerCapabilities,
WorkspaceFolder
} from "vscode-languageserver";
import { QuickPickItem } from "vscode";
import { GraphQLWorkspace } from "./workspace";
Expand All @@ -17,6 +18,12 @@ const connection = createConnection(ProposedFeatures.all);

let hasWorkspaceFolderCapability = false;

// Awaitable promise for sending messages before the connection is initialized
let initializeConnection: () => void;
const whenConnectionInitialized: Promise<void> = new Promise(
resolve => (initializeConnection = resolve)
);

const workspace = new GraphQLWorkspace(
new LanguageServerLoadingHandler(connection),
{
Expand All @@ -43,13 +50,23 @@ workspace.onSchemaTags(params => {
);
});

connection.onInitialize(async params => {
let capabilities = params.capabilities;
workspace.onConfigFilesFound(async params => {
await whenConnectionInitialized;

connection.sendNotification(
"apollographql/configFilesFound",
params instanceof Error
? // Can't stringify Errors, just results in "{}"
JSON.stringify({ message: params.message, stack: params.stack })
: JSON.stringify(params)
);
});

connection.onInitialize(async ({ capabilities, workspaceFolders }) => {
hasWorkspaceFolderCapability = !!(
capabilities.workspace && capabilities.workspace.workspaceFolders
);

const workspaceFolders = params.workspaceFolders;
if (workspaceFolders) {
// We wait until all projects are added, because after `initialize` returns we can get additional requests
// like `textDocument/codeLens`, and that way these can await `GraphQLProject#whenReady` to make sure
Expand Down Expand Up @@ -82,6 +99,7 @@ connection.onInitialize(async params => {
});

connection.onInitialized(async () => {
initializeConnection();
if (hasWorkspaceFolderCapability) {
connection.workspace.onDidChangeWorkspaceFolders(async event => {
await Promise.all([
Expand All @@ -108,48 +126,25 @@ documents.onDidChangeContent(params => {
});

connection.onDidChangeWatchedFiles(params => {
for (const change of params.changes) {
const uri = change.uri;

// FIXME: Re-enable updating projects when config files change.

// const filePath = Uri.parse(change.uri).fsPath;
// if (
// filePath.endsWith("apollo.config.js") ||
// filePath.endsWith("package.json")
// ) {
// const projectForConfig = Array.from(
// workspace.projectsByFolderUri.values()
// )
// .flatMap(arr => arr)
// .find(proj => {
// return proj.configFile === filePath;
// });

// if (projectForConfig) {
// const newConfig = findAndLoadConfig(
// dirname(projectForConfig.configFile),
// false,
// true
// );

// if (newConfig) {
// projectForConfig.updateConfig(newConfig);
// }
// }
// }
for (const { uri, type } of params.changes) {
if (
uri.endsWith("apollo.config.js") ||
uri.endsWith("package.json") ||
uri.endsWith(".env")
) {
workspace.reloadProjectForConfig(uri);
}

// Don't respond to changes in files that are currently open,
// because we'll get content change notifications instead

if (change.type === FileChangeType.Changed) {
if (type === FileChangeType.Changed) {
continue;
}

const project = workspace.projectForFile(uri);
if (!project) continue;

switch (change.type) {
switch (type) {
case FileChangeType.Created:
project.fileDidChange(uri);
break;
Expand Down
Loading