@graphql-inspector/validate-command 3.1.4 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{dist/index.d.ts → index.d.ts} +7 -1
- package/{dist/index.js → index.js} +33 -15
- package/{dist/index.mjs → index.mjs} +33 -15
- package/package.json +30 -33
- package/__tests__/validate-command.ts +0 -135
- package/dist/package.json +0 -48
- package/src/index.ts +0 -350
|
@@ -2,7 +2,7 @@ import { GlobalArgs, CommandFactory } from '@graphql-inspector/commands';
|
|
|
2
2
|
import { Source as DocumentSource } from '@graphql-tools/utils';
|
|
3
3
|
import { GraphQLSchema } from 'graphql';
|
|
4
4
|
export { CommandFactory };
|
|
5
|
-
export declare function handler({ schema, documents, strictFragments, maxDepth, apollo, keepClientFields, failOnDeprecated, filter, onlyErrors, relativePaths, output, silent, }: {
|
|
5
|
+
export declare function handler({ schema, documents, strictFragments, maxDepth, maxDirectiveCount, maxAliasCount, maxTokenCount, apollo, keepClientFields, failOnDeprecated, filter, onlyErrors, relativePaths, output, silent, }: {
|
|
6
6
|
schema: GraphQLSchema;
|
|
7
7
|
documents: DocumentSource[];
|
|
8
8
|
failOnDeprecated: boolean;
|
|
@@ -10,6 +10,9 @@ export declare function handler({ schema, documents, strictFragments, maxDepth,
|
|
|
10
10
|
apollo: boolean;
|
|
11
11
|
keepClientFields: boolean;
|
|
12
12
|
maxDepth?: number;
|
|
13
|
+
maxDirectiveCount?: number;
|
|
14
|
+
maxAliasCount?: number;
|
|
15
|
+
maxTokenCount?: number;
|
|
13
16
|
filter?: string[];
|
|
14
17
|
onlyErrors?: boolean;
|
|
15
18
|
relativePaths?: boolean;
|
|
@@ -24,6 +27,9 @@ declare const _default: CommandFactory<{}, {
|
|
|
24
27
|
apollo: boolean;
|
|
25
28
|
keepClientFields: boolean;
|
|
26
29
|
maxDepth?: number | undefined;
|
|
30
|
+
maxAliasCount?: number | undefined;
|
|
31
|
+
maxDirectiveCount?: number | undefined;
|
|
32
|
+
maxTokenCount?: number | undefined;
|
|
27
33
|
filter?: string[] | undefined;
|
|
28
34
|
onlyErrors?: boolean | undefined;
|
|
29
35
|
relativePaths?: boolean | undefined;
|
|
@@ -10,10 +10,13 @@ const path = require('path');
|
|
|
10
10
|
const fs = require('fs');
|
|
11
11
|
const graphql = require('graphql');
|
|
12
12
|
|
|
13
|
-
function handler({ schema, documents, strictFragments, maxDepth, apollo, keepClientFields, failOnDeprecated, filter, onlyErrors, relativePaths, output, silent, }) {
|
|
14
|
-
let invalidDocuments = core.validate(schema, documents.map(
|
|
13
|
+
function handler({ schema, documents, strictFragments, maxDepth, maxDirectiveCount, maxAliasCount, maxTokenCount, apollo, keepClientFields, failOnDeprecated, filter, onlyErrors, relativePaths, output, silent, }) {
|
|
14
|
+
let invalidDocuments = core.validate(schema, documents.map(doc => new graphql.Source(graphql.print(doc.document), doc.location)), {
|
|
15
15
|
strictFragments,
|
|
16
16
|
maxDepth,
|
|
17
|
+
maxAliasCount,
|
|
18
|
+
maxDirectiveCount,
|
|
19
|
+
maxTokenCount,
|
|
17
20
|
apollo,
|
|
18
21
|
keepClientFields,
|
|
19
22
|
});
|
|
@@ -59,7 +62,7 @@ function handler({ schema, documents, strictFragments, maxDepth, apollo, keepCli
|
|
|
59
62
|
}
|
|
60
63
|
}
|
|
61
64
|
function moveDeprecatedToErrors(docs) {
|
|
62
|
-
return docs.map(
|
|
65
|
+
return docs.map(doc => {
|
|
63
66
|
var _a, _b;
|
|
64
67
|
return ({
|
|
65
68
|
source: doc.source,
|
|
@@ -69,7 +72,7 @@ function moveDeprecatedToErrors(docs) {
|
|
|
69
72
|
});
|
|
70
73
|
}
|
|
71
74
|
function useRelativePaths(docs) {
|
|
72
|
-
return docs.map(
|
|
75
|
+
return docs.map(doc => {
|
|
73
76
|
doc.source.name = path.relative(process.cwd(), doc.source.name);
|
|
74
77
|
return doc;
|
|
75
78
|
});
|
|
@@ -78,9 +81,9 @@ function useFilter(docs, patterns) {
|
|
|
78
81
|
if (!patterns || !patterns.length) {
|
|
79
82
|
return docs;
|
|
80
83
|
}
|
|
81
|
-
return docs.filter(
|
|
84
|
+
return docs.filter(doc => patterns.some(filepath => doc.source.name.includes(filepath)));
|
|
82
85
|
}
|
|
83
|
-
const index = commands.createCommand(
|
|
86
|
+
const index = commands.createCommand(api => {
|
|
84
87
|
const { loaders } = api;
|
|
85
88
|
return {
|
|
86
89
|
command: 'validate <documents> <schema>',
|
|
@@ -113,6 +116,18 @@ const index = commands.createCommand((api) => {
|
|
|
113
116
|
describe: 'Fail on deep operations',
|
|
114
117
|
type: 'number',
|
|
115
118
|
},
|
|
119
|
+
maxAliasCount: {
|
|
120
|
+
describe: 'Fail on operations with too many aliases',
|
|
121
|
+
type: 'number',
|
|
122
|
+
},
|
|
123
|
+
maxDirectiveCount: {
|
|
124
|
+
describe: 'Fail on operations with too many directives',
|
|
125
|
+
type: 'number',
|
|
126
|
+
},
|
|
127
|
+
maxTokenCount: {
|
|
128
|
+
describe: 'Fail on operations with too many tokens',
|
|
129
|
+
type: 'number',
|
|
130
|
+
},
|
|
116
131
|
apollo: {
|
|
117
132
|
describe: 'Support Apollo directives',
|
|
118
133
|
type: 'boolean',
|
|
@@ -162,7 +177,10 @@ const index = commands.createCommand((api) => {
|
|
|
162
177
|
const aws = args.aws || false;
|
|
163
178
|
const apolloFederation = args.federation || false;
|
|
164
179
|
const method = ((_a = args.method) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'POST';
|
|
165
|
-
const maxDepth = args.maxDepth
|
|
180
|
+
const maxDepth = args.maxDepth != null ? args.maxDepth : undefined;
|
|
181
|
+
const maxAliasCount = args.maxAliasCount != null ? args.maxAliasCount : undefined;
|
|
182
|
+
const maxDirectiveCount = args.maxDirectiveCount != null ? args.maxDirectiveCount : undefined;
|
|
183
|
+
const maxTokenCount = args.maxTokenCount != null ? args.maxTokenCount : undefined;
|
|
166
184
|
const strictFragments = !args.noStrictFragments;
|
|
167
185
|
const keepClientFields = args.keepClientFields || false;
|
|
168
186
|
const failOnDeprecated = args.deprecated;
|
|
@@ -184,6 +202,9 @@ const index = commands.createCommand((api) => {
|
|
|
184
202
|
documents,
|
|
185
203
|
apollo,
|
|
186
204
|
maxDepth,
|
|
205
|
+
maxAliasCount,
|
|
206
|
+
maxDirectiveCount,
|
|
207
|
+
maxTokenCount,
|
|
187
208
|
strictFragments,
|
|
188
209
|
keepClientFields,
|
|
189
210
|
failOnDeprecated,
|
|
@@ -199,14 +220,13 @@ const index = commands.createCommand((api) => {
|
|
|
199
220
|
});
|
|
200
221
|
function countErrors(invalidDocuments) {
|
|
201
222
|
if (invalidDocuments.length) {
|
|
202
|
-
return invalidDocuments.filter(
|
|
203
|
-
.length;
|
|
223
|
+
return invalidDocuments.filter(doc => doc.errors && doc.errors.length).length;
|
|
204
224
|
}
|
|
205
225
|
return 0;
|
|
206
226
|
}
|
|
207
227
|
function countDeprecated(invalidDocuments) {
|
|
208
228
|
if (invalidDocuments.length) {
|
|
209
|
-
return invalidDocuments.filter(
|
|
229
|
+
return invalidDocuments.filter(doc => doc.deprecated && doc.deprecated.length).length;
|
|
210
230
|
}
|
|
211
231
|
return 0;
|
|
212
232
|
}
|
|
@@ -214,18 +234,16 @@ function printInvalidDocuments(invalidDocuments, listKey, isError = false, silen
|
|
|
214
234
|
if (silent) {
|
|
215
235
|
return;
|
|
216
236
|
}
|
|
217
|
-
invalidDocuments.forEach(
|
|
237
|
+
invalidDocuments.forEach(doc => {
|
|
218
238
|
if (doc.errors.length) {
|
|
219
|
-
renderErrors(doc.source.name, doc[listKey], isError).forEach(
|
|
239
|
+
renderErrors(doc.source.name, doc[listKey], isError).forEach(line => {
|
|
220
240
|
logger.Logger.log(line);
|
|
221
241
|
});
|
|
222
242
|
}
|
|
223
243
|
});
|
|
224
244
|
}
|
|
225
245
|
function renderErrors(sourceName, errors, isError = false) {
|
|
226
|
-
const errorsAsString = errors
|
|
227
|
-
.map((e) => ` - ${logger.bolderize(e.message)}`)
|
|
228
|
-
.join('\n');
|
|
246
|
+
const errorsAsString = errors.map(e => ` - ${logger.bolderize(e.message)}`).join('\n');
|
|
229
247
|
return [
|
|
230
248
|
isError ? logger.chalk.redBright('error') : logger.chalk.yellowBright('warn'),
|
|
231
249
|
`in ${sourceName}:\n\n`,
|
|
@@ -6,10 +6,13 @@ import { relative } from 'path';
|
|
|
6
6
|
import { writeFileSync } from 'fs';
|
|
7
7
|
import { Source, print } from 'graphql';
|
|
8
8
|
|
|
9
|
-
function handler({ schema, documents, strictFragments, maxDepth, apollo, keepClientFields, failOnDeprecated, filter, onlyErrors, relativePaths, output, silent, }) {
|
|
10
|
-
let invalidDocuments = validate(schema, documents.map(
|
|
9
|
+
function handler({ schema, documents, strictFragments, maxDepth, maxDirectiveCount, maxAliasCount, maxTokenCount, apollo, keepClientFields, failOnDeprecated, filter, onlyErrors, relativePaths, output, silent, }) {
|
|
10
|
+
let invalidDocuments = validate(schema, documents.map(doc => new Source(print(doc.document), doc.location)), {
|
|
11
11
|
strictFragments,
|
|
12
12
|
maxDepth,
|
|
13
|
+
maxAliasCount,
|
|
14
|
+
maxDirectiveCount,
|
|
15
|
+
maxTokenCount,
|
|
13
16
|
apollo,
|
|
14
17
|
keepClientFields,
|
|
15
18
|
});
|
|
@@ -55,7 +58,7 @@ function handler({ schema, documents, strictFragments, maxDepth, apollo, keepCli
|
|
|
55
58
|
}
|
|
56
59
|
}
|
|
57
60
|
function moveDeprecatedToErrors(docs) {
|
|
58
|
-
return docs.map(
|
|
61
|
+
return docs.map(doc => {
|
|
59
62
|
var _a, _b;
|
|
60
63
|
return ({
|
|
61
64
|
source: doc.source,
|
|
@@ -65,7 +68,7 @@ function moveDeprecatedToErrors(docs) {
|
|
|
65
68
|
});
|
|
66
69
|
}
|
|
67
70
|
function useRelativePaths(docs) {
|
|
68
|
-
return docs.map(
|
|
71
|
+
return docs.map(doc => {
|
|
69
72
|
doc.source.name = relative(process.cwd(), doc.source.name);
|
|
70
73
|
return doc;
|
|
71
74
|
});
|
|
@@ -74,9 +77,9 @@ function useFilter(docs, patterns) {
|
|
|
74
77
|
if (!patterns || !patterns.length) {
|
|
75
78
|
return docs;
|
|
76
79
|
}
|
|
77
|
-
return docs.filter(
|
|
80
|
+
return docs.filter(doc => patterns.some(filepath => doc.source.name.includes(filepath)));
|
|
78
81
|
}
|
|
79
|
-
const index = createCommand(
|
|
82
|
+
const index = createCommand(api => {
|
|
80
83
|
const { loaders } = api;
|
|
81
84
|
return {
|
|
82
85
|
command: 'validate <documents> <schema>',
|
|
@@ -109,6 +112,18 @@ const index = createCommand((api) => {
|
|
|
109
112
|
describe: 'Fail on deep operations',
|
|
110
113
|
type: 'number',
|
|
111
114
|
},
|
|
115
|
+
maxAliasCount: {
|
|
116
|
+
describe: 'Fail on operations with too many aliases',
|
|
117
|
+
type: 'number',
|
|
118
|
+
},
|
|
119
|
+
maxDirectiveCount: {
|
|
120
|
+
describe: 'Fail on operations with too many directives',
|
|
121
|
+
type: 'number',
|
|
122
|
+
},
|
|
123
|
+
maxTokenCount: {
|
|
124
|
+
describe: 'Fail on operations with too many tokens',
|
|
125
|
+
type: 'number',
|
|
126
|
+
},
|
|
112
127
|
apollo: {
|
|
113
128
|
describe: 'Support Apollo directives',
|
|
114
129
|
type: 'boolean',
|
|
@@ -158,7 +173,10 @@ const index = createCommand((api) => {
|
|
|
158
173
|
const aws = args.aws || false;
|
|
159
174
|
const apolloFederation = args.federation || false;
|
|
160
175
|
const method = ((_a = args.method) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'POST';
|
|
161
|
-
const maxDepth = args.maxDepth
|
|
176
|
+
const maxDepth = args.maxDepth != null ? args.maxDepth : undefined;
|
|
177
|
+
const maxAliasCount = args.maxAliasCount != null ? args.maxAliasCount : undefined;
|
|
178
|
+
const maxDirectiveCount = args.maxDirectiveCount != null ? args.maxDirectiveCount : undefined;
|
|
179
|
+
const maxTokenCount = args.maxTokenCount != null ? args.maxTokenCount : undefined;
|
|
162
180
|
const strictFragments = !args.noStrictFragments;
|
|
163
181
|
const keepClientFields = args.keepClientFields || false;
|
|
164
182
|
const failOnDeprecated = args.deprecated;
|
|
@@ -180,6 +198,9 @@ const index = createCommand((api) => {
|
|
|
180
198
|
documents,
|
|
181
199
|
apollo,
|
|
182
200
|
maxDepth,
|
|
201
|
+
maxAliasCount,
|
|
202
|
+
maxDirectiveCount,
|
|
203
|
+
maxTokenCount,
|
|
183
204
|
strictFragments,
|
|
184
205
|
keepClientFields,
|
|
185
206
|
failOnDeprecated,
|
|
@@ -195,14 +216,13 @@ const index = createCommand((api) => {
|
|
|
195
216
|
});
|
|
196
217
|
function countErrors(invalidDocuments) {
|
|
197
218
|
if (invalidDocuments.length) {
|
|
198
|
-
return invalidDocuments.filter(
|
|
199
|
-
.length;
|
|
219
|
+
return invalidDocuments.filter(doc => doc.errors && doc.errors.length).length;
|
|
200
220
|
}
|
|
201
221
|
return 0;
|
|
202
222
|
}
|
|
203
223
|
function countDeprecated(invalidDocuments) {
|
|
204
224
|
if (invalidDocuments.length) {
|
|
205
|
-
return invalidDocuments.filter(
|
|
225
|
+
return invalidDocuments.filter(doc => doc.deprecated && doc.deprecated.length).length;
|
|
206
226
|
}
|
|
207
227
|
return 0;
|
|
208
228
|
}
|
|
@@ -210,18 +230,16 @@ function printInvalidDocuments(invalidDocuments, listKey, isError = false, silen
|
|
|
210
230
|
if (silent) {
|
|
211
231
|
return;
|
|
212
232
|
}
|
|
213
|
-
invalidDocuments.forEach(
|
|
233
|
+
invalidDocuments.forEach(doc => {
|
|
214
234
|
if (doc.errors.length) {
|
|
215
|
-
renderErrors(doc.source.name, doc[listKey], isError).forEach(
|
|
235
|
+
renderErrors(doc.source.name, doc[listKey], isError).forEach(line => {
|
|
216
236
|
Logger.log(line);
|
|
217
237
|
});
|
|
218
238
|
}
|
|
219
239
|
});
|
|
220
240
|
}
|
|
221
241
|
function renderErrors(sourceName, errors, isError = false) {
|
|
222
|
-
const errorsAsString = errors
|
|
223
|
-
.map((e) => ` - ${bolderize(e.message)}`)
|
|
224
|
-
.join('\n');
|
|
242
|
+
const errorsAsString = errors.map(e => ` - ${bolderize(e.message)}`).join('\n');
|
|
225
243
|
return [
|
|
226
244
|
isError ? chalk.redBright('error') : chalk.yellowBright('warn'),
|
|
227
245
|
`in ${sourceName}:\n\n`,
|
package/package.json
CHANGED
|
@@ -1,51 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphql-inspector/validate-command",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "Validate Documents in GraphQL Inspector",
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"peerDependencies": {
|
|
7
|
+
"graphql": "^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@graphql-inspector/commands": "3.4.0",
|
|
11
|
+
"@graphql-inspector/core": "3.4.0",
|
|
12
|
+
"@graphql-inspector/logger": "3.4.0",
|
|
13
|
+
"tslib": "^2.0.0"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "kamilkisiela/graphql-inspector",
|
|
18
|
+
"directory": "packages/commands/validate"
|
|
19
|
+
},
|
|
5
20
|
"keywords": [
|
|
6
21
|
"graphql",
|
|
7
22
|
"graphql-inspector",
|
|
8
23
|
"graphql-inspector-command",
|
|
9
24
|
"tools"
|
|
10
25
|
],
|
|
11
|
-
"sideEffects": false,
|
|
12
|
-
"main": "dist/index.js",
|
|
13
|
-
"module": "dist/index.mjs",
|
|
14
|
-
"exports": {
|
|
15
|
-
".": {
|
|
16
|
-
"require": "./dist/index.js",
|
|
17
|
-
"import": "./dist/index.mjs"
|
|
18
|
-
},
|
|
19
|
-
"./*": {
|
|
20
|
-
"require": "./dist/*.js",
|
|
21
|
-
"import": "./dist/*.mjs"
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
"typings": "dist/index.d.ts",
|
|
25
|
-
"typescript": {
|
|
26
|
-
"definition": "dist/index.d.ts"
|
|
27
|
-
},
|
|
28
26
|
"author": {
|
|
29
27
|
"name": "Kamil Kisiela",
|
|
30
28
|
"email": "kamil.kisiela@gmail.com",
|
|
31
29
|
"url": "https://github.com/kamilkisiela"
|
|
32
30
|
},
|
|
33
31
|
"license": "MIT",
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"peerDependencies": {
|
|
40
|
-
"graphql": "^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
41
|
-
},
|
|
42
|
-
"dependencies": {
|
|
43
|
-
"@graphql-inspector/commands": "3.1.4",
|
|
44
|
-
"@graphql-inspector/core": "3.1.4",
|
|
45
|
-
"@graphql-inspector/logger": "3.1.4",
|
|
46
|
-
"tslib": "^2.0.0"
|
|
32
|
+
"main": "index.js",
|
|
33
|
+
"module": "index.mjs",
|
|
34
|
+
"typings": "index.d.ts",
|
|
35
|
+
"typescript": {
|
|
36
|
+
"definition": "index.d.ts"
|
|
47
37
|
},
|
|
48
|
-
"
|
|
49
|
-
"
|
|
38
|
+
"exports": {
|
|
39
|
+
".": {
|
|
40
|
+
"require": "./index.js",
|
|
41
|
+
"import": "./index.mjs"
|
|
42
|
+
},
|
|
43
|
+
"./*": {
|
|
44
|
+
"require": "./*.js",
|
|
45
|
+
"import": "./*.mjs"
|
|
46
|
+
}
|
|
50
47
|
}
|
|
51
48
|
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import '@graphql-inspector/testing';
|
|
2
|
-
import yargs from 'yargs';
|
|
3
|
-
import { buildSchema, parse } from 'graphql';
|
|
4
|
-
import { relative } from 'path';
|
|
5
|
-
import { mockCommand } from '@graphql-inspector/commands';
|
|
6
|
-
import { mockLogger, unmockLogger } from '@graphql-inspector/logger';
|
|
7
|
-
import createCommand from '../src';
|
|
8
|
-
|
|
9
|
-
const schema = buildSchema(/* GraphQL */ `
|
|
10
|
-
type Post {
|
|
11
|
-
id: ID
|
|
12
|
-
title: String
|
|
13
|
-
createdAt: String
|
|
14
|
-
modifiedAt: String
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
type Query {
|
|
18
|
-
post: Post!
|
|
19
|
-
}
|
|
20
|
-
`);
|
|
21
|
-
|
|
22
|
-
const operation = parse(/* GraphQL */ `
|
|
23
|
-
query post {
|
|
24
|
-
post {
|
|
25
|
-
id
|
|
26
|
-
title
|
|
27
|
-
createdAtSomePoint
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
`);
|
|
31
|
-
|
|
32
|
-
const validate = createCommand({
|
|
33
|
-
config: {
|
|
34
|
-
use: {
|
|
35
|
-
commands: [],
|
|
36
|
-
loaders: [],
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
loaders: {
|
|
40
|
-
async loadSchema() {
|
|
41
|
-
return schema;
|
|
42
|
-
},
|
|
43
|
-
async loadDocuments() {
|
|
44
|
-
return [
|
|
45
|
-
{
|
|
46
|
-
document: operation,
|
|
47
|
-
location: 'document.graphql',
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
document: operation,
|
|
51
|
-
location: 'document2.graphql',
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
document: parse(/* GraphQL */ `
|
|
55
|
-
query post {
|
|
56
|
-
post {
|
|
57
|
-
id
|
|
58
|
-
title
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
`),
|
|
62
|
-
location: 'valid-document.graphql',
|
|
63
|
-
},
|
|
64
|
-
];
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
describe('validate', () => {
|
|
70
|
-
let spyReporter: jest.SpyInstance;
|
|
71
|
-
let spyProcessExit: jest.SpyInstance;
|
|
72
|
-
let spyProcessCwd: jest.SpyInstance;
|
|
73
|
-
|
|
74
|
-
beforeEach(() => {
|
|
75
|
-
spyProcessExit = jest.spyOn(process, 'exit');
|
|
76
|
-
spyProcessExit.mockImplementation();
|
|
77
|
-
|
|
78
|
-
spyProcessCwd = jest
|
|
79
|
-
.spyOn(process, 'cwd')
|
|
80
|
-
.mockImplementation(() => __dirname);
|
|
81
|
-
|
|
82
|
-
spyReporter = jest.fn();
|
|
83
|
-
mockLogger(spyReporter as any);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
afterEach(() => {
|
|
87
|
-
spyProcessExit.mockRestore();
|
|
88
|
-
spyProcessCwd.mockRestore();
|
|
89
|
-
spyReporter.mockRestore();
|
|
90
|
-
unmockLogger();
|
|
91
|
-
yargs.reset();
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test('should load graphql files', async () => {
|
|
95
|
-
await mockCommand(validate, 'validate "*.graphql" schema.graphql');
|
|
96
|
-
|
|
97
|
-
expect(spyReporter).toHaveBeenCalledNormalized(
|
|
98
|
-
'Detected 2 invalid documents:',
|
|
99
|
-
);
|
|
100
|
-
expect(spyReporter).toHaveBeenCalledNormalized('document.graphql:');
|
|
101
|
-
expect(spyReporter).toHaveBeenCalledNormalized(
|
|
102
|
-
'Cannot query field createdAtSomePoint on type Post',
|
|
103
|
-
);
|
|
104
|
-
expect(spyReporter).not.toHaveBeenCalledNormalized(
|
|
105
|
-
'All documents are valid',
|
|
106
|
-
);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test('should allow to filter results by file paths', async () => {
|
|
110
|
-
await mockCommand(
|
|
111
|
-
validate,
|
|
112
|
-
'validate "*.graphql" schema.graphql --filter document2.graphql',
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
expect(spyReporter).not.toHaveBeenCalledNormalized('document.graphql:');
|
|
116
|
-
expect(spyReporter).toHaveBeenCalledNormalized('document2.graphql:');
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
test('should allow to show relative paths', async () => {
|
|
120
|
-
await mockCommand(
|
|
121
|
-
validate,
|
|
122
|
-
'validate "*.graphql" schema.graphql --relativePaths',
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
expect(spyReporter).toHaveBeenCalledNormalized(
|
|
126
|
-
`in ${relative(process.cwd(), 'document.graphql')}:`,
|
|
127
|
-
);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
test('should allow for silent mode', async () => {
|
|
131
|
-
await mockCommand(validate, 'validate "*.graphql" schema.graphql --silent');
|
|
132
|
-
|
|
133
|
-
expect(spyReporter).not.toHaveBeenCalledNormalized('document.graphql:');
|
|
134
|
-
});
|
|
135
|
-
});
|
package/dist/package.json
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@graphql-inspector/validate-command",
|
|
3
|
-
"version": "3.1.4",
|
|
4
|
-
"description": "Validate Documents in GraphQL Inspector",
|
|
5
|
-
"sideEffects": false,
|
|
6
|
-
"peerDependencies": {
|
|
7
|
-
"graphql": "^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
8
|
-
},
|
|
9
|
-
"dependencies": {
|
|
10
|
-
"@graphql-inspector/commands": "3.1.4",
|
|
11
|
-
"@graphql-inspector/core": "3.1.4",
|
|
12
|
-
"@graphql-inspector/logger": "3.1.4",
|
|
13
|
-
"tslib": "^2.0.0"
|
|
14
|
-
},
|
|
15
|
-
"repository": {
|
|
16
|
-
"type": "git",
|
|
17
|
-
"url": "kamilkisiela/graphql-inspector",
|
|
18
|
-
"directory": "packages/commands/validate"
|
|
19
|
-
},
|
|
20
|
-
"keywords": [
|
|
21
|
-
"graphql",
|
|
22
|
-
"graphql-inspector",
|
|
23
|
-
"graphql-inspector-command",
|
|
24
|
-
"tools"
|
|
25
|
-
],
|
|
26
|
-
"author": {
|
|
27
|
-
"name": "Kamil Kisiela",
|
|
28
|
-
"email": "kamil.kisiela@gmail.com",
|
|
29
|
-
"url": "https://github.com/kamilkisiela"
|
|
30
|
-
},
|
|
31
|
-
"license": "MIT",
|
|
32
|
-
"main": "index.js",
|
|
33
|
-
"module": "index.mjs",
|
|
34
|
-
"typings": "index.d.ts",
|
|
35
|
-
"typescript": {
|
|
36
|
-
"definition": "index.d.ts"
|
|
37
|
-
},
|
|
38
|
-
"exports": {
|
|
39
|
-
".": {
|
|
40
|
-
"require": "./index.js",
|
|
41
|
-
"import": "./index.mjs"
|
|
42
|
-
},
|
|
43
|
-
"./*": {
|
|
44
|
-
"require": "./*.js",
|
|
45
|
-
"import": "./*.mjs"
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,350 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createCommand,
|
|
3
|
-
GlobalArgs,
|
|
4
|
-
parseGlobalArgs,
|
|
5
|
-
CommandFactory,
|
|
6
|
-
} from '@graphql-inspector/commands';
|
|
7
|
-
import { Logger, bolderize, chalk } from '@graphql-inspector/logger';
|
|
8
|
-
import {
|
|
9
|
-
validate as validateDocuments,
|
|
10
|
-
InvalidDocument,
|
|
11
|
-
} from '@graphql-inspector/core';
|
|
12
|
-
import { Source as DocumentSource } from '@graphql-tools/utils';
|
|
13
|
-
import { relative } from 'path';
|
|
14
|
-
import { writeFileSync } from 'fs';
|
|
15
|
-
import { Source, print, GraphQLSchema, GraphQLError } from 'graphql';
|
|
16
|
-
|
|
17
|
-
export { CommandFactory };
|
|
18
|
-
|
|
19
|
-
export function handler({
|
|
20
|
-
schema,
|
|
21
|
-
documents,
|
|
22
|
-
strictFragments,
|
|
23
|
-
maxDepth,
|
|
24
|
-
apollo,
|
|
25
|
-
keepClientFields,
|
|
26
|
-
failOnDeprecated,
|
|
27
|
-
filter,
|
|
28
|
-
onlyErrors,
|
|
29
|
-
relativePaths,
|
|
30
|
-
output,
|
|
31
|
-
silent,
|
|
32
|
-
}: {
|
|
33
|
-
schema: GraphQLSchema;
|
|
34
|
-
documents: DocumentSource[];
|
|
35
|
-
failOnDeprecated: boolean;
|
|
36
|
-
strictFragments: boolean;
|
|
37
|
-
apollo: boolean;
|
|
38
|
-
keepClientFields: boolean;
|
|
39
|
-
maxDepth?: number;
|
|
40
|
-
filter?: string[];
|
|
41
|
-
onlyErrors?: boolean;
|
|
42
|
-
relativePaths?: boolean;
|
|
43
|
-
output?: string;
|
|
44
|
-
silent?: boolean;
|
|
45
|
-
}) {
|
|
46
|
-
let invalidDocuments = validateDocuments(
|
|
47
|
-
schema,
|
|
48
|
-
documents.map((doc) => new Source(print(doc.document!), doc.location)),
|
|
49
|
-
{
|
|
50
|
-
strictFragments,
|
|
51
|
-
maxDepth,
|
|
52
|
-
apollo,
|
|
53
|
-
keepClientFields,
|
|
54
|
-
},
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
if (!invalidDocuments.length) {
|
|
58
|
-
Logger.success('All documents are valid');
|
|
59
|
-
} else {
|
|
60
|
-
if (failOnDeprecated) {
|
|
61
|
-
invalidDocuments = moveDeprecatedToErrors(invalidDocuments);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (relativePaths) {
|
|
65
|
-
invalidDocuments = useRelativePaths(invalidDocuments);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const errorsCount = countErrors(invalidDocuments);
|
|
69
|
-
const deprecated = countDeprecated(invalidDocuments);
|
|
70
|
-
const shouldFailProcess = errorsCount > 0;
|
|
71
|
-
|
|
72
|
-
if (errorsCount) {
|
|
73
|
-
if (!silent) {
|
|
74
|
-
Logger.log(
|
|
75
|
-
`\nDetected ${errorsCount} invalid document${
|
|
76
|
-
errorsCount > 1 ? 's' : ''
|
|
77
|
-
}:\n`,
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
printInvalidDocuments(
|
|
82
|
-
useFilter(invalidDocuments, filter),
|
|
83
|
-
'errors',
|
|
84
|
-
true,
|
|
85
|
-
silent,
|
|
86
|
-
);
|
|
87
|
-
} else {
|
|
88
|
-
Logger.success('All documents are valid');
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (deprecated && !onlyErrors) {
|
|
92
|
-
if (!silent) {
|
|
93
|
-
Logger.info(
|
|
94
|
-
`\nDetected ${deprecated} document${
|
|
95
|
-
deprecated > 1 ? 's' : ''
|
|
96
|
-
} with deprecated fields:\n`,
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
printInvalidDocuments(
|
|
101
|
-
useFilter(invalidDocuments, filter),
|
|
102
|
-
'deprecated',
|
|
103
|
-
false,
|
|
104
|
-
silent,
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (output) {
|
|
109
|
-
writeFileSync(
|
|
110
|
-
output,
|
|
111
|
-
JSON.stringify(
|
|
112
|
-
{
|
|
113
|
-
status: !shouldFailProcess,
|
|
114
|
-
documents: useFilter(invalidDocuments, filter),
|
|
115
|
-
},
|
|
116
|
-
null,
|
|
117
|
-
2,
|
|
118
|
-
),
|
|
119
|
-
{
|
|
120
|
-
encoding: 'utf-8',
|
|
121
|
-
},
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (shouldFailProcess) {
|
|
126
|
-
process.exit(1);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function moveDeprecatedToErrors(docs: InvalidDocument[]) {
|
|
132
|
-
return docs.map((doc) => ({
|
|
133
|
-
source: doc.source,
|
|
134
|
-
errors: [...(doc.errors ?? []), ...(doc.deprecated ?? [])],
|
|
135
|
-
deprecated: [],
|
|
136
|
-
}));
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function useRelativePaths(docs: InvalidDocument[]) {
|
|
140
|
-
return docs.map((doc) => {
|
|
141
|
-
doc.source.name = relative(process.cwd(), doc.source.name);
|
|
142
|
-
return doc;
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function useFilter(docs: InvalidDocument[], patterns?: string[]) {
|
|
147
|
-
if (!patterns || !patterns.length) {
|
|
148
|
-
return docs;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return docs.filter((doc) =>
|
|
152
|
-
patterns.some((filepath) => doc.source.name.includes(filepath)),
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export default createCommand<
|
|
157
|
-
{},
|
|
158
|
-
{
|
|
159
|
-
schema: string;
|
|
160
|
-
documents: string;
|
|
161
|
-
deprecated: boolean;
|
|
162
|
-
noStrictFragments: boolean;
|
|
163
|
-
apollo: boolean;
|
|
164
|
-
keepClientFields: boolean;
|
|
165
|
-
maxDepth?: number;
|
|
166
|
-
filter?: string[];
|
|
167
|
-
onlyErrors?: boolean;
|
|
168
|
-
relativePaths?: boolean;
|
|
169
|
-
output?: string;
|
|
170
|
-
silent?: boolean;
|
|
171
|
-
ignore?: string[];
|
|
172
|
-
} & GlobalArgs
|
|
173
|
-
>((api) => {
|
|
174
|
-
const { loaders } = api;
|
|
175
|
-
|
|
176
|
-
return {
|
|
177
|
-
command: 'validate <documents> <schema>',
|
|
178
|
-
describe: 'Validate Fragments and Operations',
|
|
179
|
-
builder(yargs) {
|
|
180
|
-
return yargs
|
|
181
|
-
.positional('schema', {
|
|
182
|
-
describe: 'Point to a schema',
|
|
183
|
-
type: 'string',
|
|
184
|
-
demandOption: true,
|
|
185
|
-
})
|
|
186
|
-
.positional('documents', {
|
|
187
|
-
describe: 'Point to documents',
|
|
188
|
-
type: 'string',
|
|
189
|
-
demandOption: true,
|
|
190
|
-
})
|
|
191
|
-
.options({
|
|
192
|
-
deprecated: {
|
|
193
|
-
alias: 'd',
|
|
194
|
-
describe: 'Fail on deprecated usage',
|
|
195
|
-
type: 'boolean',
|
|
196
|
-
default: false,
|
|
197
|
-
},
|
|
198
|
-
noStrictFragments: {
|
|
199
|
-
describe: 'Do not fail on duplicated fragment names',
|
|
200
|
-
type: 'boolean',
|
|
201
|
-
default: false,
|
|
202
|
-
},
|
|
203
|
-
maxDepth: {
|
|
204
|
-
describe: 'Fail on deep operations',
|
|
205
|
-
type: 'number',
|
|
206
|
-
},
|
|
207
|
-
apollo: {
|
|
208
|
-
describe: 'Support Apollo directives',
|
|
209
|
-
type: 'boolean',
|
|
210
|
-
default: false,
|
|
211
|
-
},
|
|
212
|
-
keepClientFields: {
|
|
213
|
-
describe:
|
|
214
|
-
'Keeps the fields with @client, but removes @client directive from them',
|
|
215
|
-
type: 'boolean',
|
|
216
|
-
default: false,
|
|
217
|
-
},
|
|
218
|
-
filter: {
|
|
219
|
-
describe: 'Show results only from a list of files (or file)',
|
|
220
|
-
array: true,
|
|
221
|
-
type: 'string',
|
|
222
|
-
},
|
|
223
|
-
ignore: {
|
|
224
|
-
describe: 'Ignore and do not load these files (supports glob)',
|
|
225
|
-
array: true,
|
|
226
|
-
type: 'string',
|
|
227
|
-
},
|
|
228
|
-
onlyErrors: {
|
|
229
|
-
describe: 'Show only errors',
|
|
230
|
-
type: 'boolean',
|
|
231
|
-
default: false,
|
|
232
|
-
},
|
|
233
|
-
relativePaths: {
|
|
234
|
-
describe: 'Show relative paths',
|
|
235
|
-
type: 'boolean',
|
|
236
|
-
default: false,
|
|
237
|
-
},
|
|
238
|
-
silent: {
|
|
239
|
-
describe: 'Do not print results',
|
|
240
|
-
type: 'boolean',
|
|
241
|
-
default: false,
|
|
242
|
-
},
|
|
243
|
-
output: {
|
|
244
|
-
describe: 'Output JSON file',
|
|
245
|
-
type: 'string',
|
|
246
|
-
},
|
|
247
|
-
});
|
|
248
|
-
},
|
|
249
|
-
async handler(args) {
|
|
250
|
-
const { headers, token } = parseGlobalArgs(args);
|
|
251
|
-
const apollo = args.apollo || false;
|
|
252
|
-
const aws = args.aws || false;
|
|
253
|
-
const apolloFederation = args.federation || false;
|
|
254
|
-
const method = args.method?.toUpperCase() || 'POST';
|
|
255
|
-
const maxDepth = args.maxDepth || undefined;
|
|
256
|
-
const strictFragments = !args.noStrictFragments;
|
|
257
|
-
const keepClientFields = args.keepClientFields || false;
|
|
258
|
-
const failOnDeprecated = args.deprecated;
|
|
259
|
-
const output = args.output;
|
|
260
|
-
const silent = args.silent || false;
|
|
261
|
-
const relativePaths = args.relativePaths || false;
|
|
262
|
-
const onlyErrors = args.onlyErrors || false;
|
|
263
|
-
const ignore = args.ignore || [];
|
|
264
|
-
|
|
265
|
-
const schema = await loaders.loadSchema(
|
|
266
|
-
args.schema,
|
|
267
|
-
{
|
|
268
|
-
headers,
|
|
269
|
-
token,
|
|
270
|
-
method,
|
|
271
|
-
},
|
|
272
|
-
apolloFederation,
|
|
273
|
-
aws,
|
|
274
|
-
);
|
|
275
|
-
const documents = await loaders.loadDocuments(args.documents, {
|
|
276
|
-
ignore,
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
return handler({
|
|
280
|
-
schema,
|
|
281
|
-
documents,
|
|
282
|
-
apollo,
|
|
283
|
-
maxDepth,
|
|
284
|
-
strictFragments,
|
|
285
|
-
keepClientFields,
|
|
286
|
-
failOnDeprecated,
|
|
287
|
-
filter: args.filter,
|
|
288
|
-
silent,
|
|
289
|
-
output,
|
|
290
|
-
relativePaths,
|
|
291
|
-
onlyErrors,
|
|
292
|
-
});
|
|
293
|
-
},
|
|
294
|
-
};
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
function countErrors(invalidDocuments: InvalidDocument[]): number {
|
|
298
|
-
if (invalidDocuments.length) {
|
|
299
|
-
return invalidDocuments.filter((doc) => doc.errors && doc.errors.length)
|
|
300
|
-
.length;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
return 0;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
function countDeprecated(invalidDocuments: InvalidDocument[]): number {
|
|
307
|
-
if (invalidDocuments.length) {
|
|
308
|
-
return invalidDocuments.filter(
|
|
309
|
-
(doc) => doc.deprecated && doc.deprecated.length,
|
|
310
|
-
).length;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return 0;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
function printInvalidDocuments(
|
|
317
|
-
invalidDocuments: InvalidDocument[],
|
|
318
|
-
listKey: 'errors' | 'deprecated',
|
|
319
|
-
isError = false,
|
|
320
|
-
silent = false,
|
|
321
|
-
): void {
|
|
322
|
-
if (silent) {
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
invalidDocuments.forEach((doc) => {
|
|
327
|
-
if (doc.errors.length) {
|
|
328
|
-
renderErrors(doc.source.name, doc[listKey], isError).forEach((line) => {
|
|
329
|
-
Logger.log(line);
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
function renderErrors(
|
|
336
|
-
sourceName: string,
|
|
337
|
-
errors: GraphQLError[],
|
|
338
|
-
isError = false,
|
|
339
|
-
): string[] {
|
|
340
|
-
const errorsAsString = errors
|
|
341
|
-
.map((e) => ` - ${bolderize(e.message)}`)
|
|
342
|
-
.join('\n');
|
|
343
|
-
|
|
344
|
-
return [
|
|
345
|
-
isError ? chalk.redBright('error') : chalk.yellowBright('warn'),
|
|
346
|
-
`in ${sourceName}:\n\n`,
|
|
347
|
-
errorsAsString,
|
|
348
|
-
'\n\n',
|
|
349
|
-
];
|
|
350
|
-
}
|