-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Copy pathclient.ts
179 lines (162 loc) · 4.93 KB
/
client.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/**
* Copyright (c) 2019 GraphQL Contributors
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*
*/
import { GraphQLSchema } from 'graphql';
import invariant from 'assert';
import fs from 'fs';
import { buildSchema, buildClientSchema } from 'graphql';
import {
getAutocompleteSuggestions,
getDiagnostics,
getOutline,
} from 'graphql-language-service-interface';
import { Position } from 'graphql-language-service-utils';
import path from 'path';
import { CompletionItem, Diagnostic } from 'graphql-language-service-types/src';
const GRAPHQL_SUCCESS_CODE = 0;
const GRAPHQL_FAILURE_CODE = 1;
type EXIT_CODE = 0 | 1;
/**
* Performs GraphQL language service features with provided arguments from
* the command-line interface.
*
* `autocomplete`: returns GraphQL autocomplete suggestions at the cursor
* location provided, or at the end of the query text.
* `outline`: returns GraphQL query outline information.
* `validate`: performs GraphQL query lint/validations and returns the results.
* Query validation is only performed if a schema path is supplied.
*/
export default function main(
command: string,
argv: { [key: string]: string },
): void {
const filePath = argv.file && argv.file.trim();
invariant(
argv.text || argv.file,
'A path to the GraphQL file or its contents is required.',
);
const text = ensureText(argv.text, filePath);
const schemaPath = argv.schemaPath && argv.schemaPath.trim();
let exitCode;
switch (command) {
case 'autocomplete':
const lines = text.split('\n');
const row = parseInt(argv.row, 10) || lines.length - 1;
const column =
parseInt(argv.column, 10) || lines[lines.length - 1].length;
const point = new Position(row, column);
exitCode = _getAutocompleteSuggestions(text, point, schemaPath);
break;
case 'outline':
exitCode = _getOutline(text);
break;
case 'validate':
exitCode = _getDiagnostics(filePath, text, schemaPath);
break;
default:
throw new Error(`Unknown command '${command}'`);
}
process.exit(exitCode);
}
interface AutocompleteResultsMap {
[i: number]: CompletionItem;
}
function _getAutocompleteSuggestions(
queryText: string,
point: Position,
schemaPath: string,
): EXIT_CODE {
invariant(
schemaPath,
'A schema path is required to provide GraphQL autocompletion',
);
try {
const schema = schemaPath ? generateSchema(schemaPath) : null;
const resultArray = schema
? getAutocompleteSuggestions(schema, queryText, point)
: [];
const resultObject: AutocompleteResultsMap = resultArray.reduce(
(prev: AutocompleteResultsMap, cur, index) => {
prev[index] = cur;
return prev;
},
{},
);
process.stdout.write(JSON.stringify(resultObject, null, 2));
return GRAPHQL_SUCCESS_CODE;
} catch (error) {
process.stderr.write(error);
return GRAPHQL_FAILURE_CODE;
}
}
interface DiagnosticResultsMap {
[i: number]: Diagnostic;
}
function _getDiagnostics(
_filePath: string,
queryText: string,
schemaPath?: string,
): EXIT_CODE {
try {
// `schema` is not strictly requied as GraphQL diagnostics may still notify
// whether the query text is syntactically valid.
const schema = schemaPath ? generateSchema(schemaPath) : null;
const resultArray = getDiagnostics(queryText, schema);
const resultObject: DiagnosticResultsMap = resultArray.reduce(
(prev: DiagnosticResultsMap, cur, index) => {
prev[index] = cur;
return prev;
},
{},
);
process.stdout.write(JSON.stringify(resultObject, null, 2));
return GRAPHQL_SUCCESS_CODE;
} catch (error) {
process.stderr.write(error);
return GRAPHQL_FAILURE_CODE;
}
}
function _getOutline(queryText: string): EXIT_CODE {
try {
const outline = getOutline(queryText);
if (outline) {
process.stdout.write(JSON.stringify(outline, null, 2));
} else {
throw Error('Error parsing or no outline tree found');
}
} catch (error) {
process.stderr.write(error);
return GRAPHQL_FAILURE_CODE;
}
return GRAPHQL_SUCCESS_CODE;
}
function ensureText(queryText: string, filePath: string): string {
let text = queryText;
// Always honor text argument over filePath.
// If text isn't available, try reading from the filePath.
if (!text) {
try {
text = fs.readFileSync(filePath, 'utf8');
} catch (error) {
throw new Error(error);
}
}
return text;
}
function generateSchema(schemaPath: string): GraphQLSchema {
const schemaDSL = fs.readFileSync(schemaPath, 'utf8');
const schemaFileExt = path.extname(schemaPath);
switch (schemaFileExt) {
case '.graphql':
return buildSchema(schemaDSL);
case '.json':
return buildClientSchema(JSON.parse(schemaDSL));
default:
throw new Error('Unsupported schema file extention');
}
}