@graphql-inspector/validate-command 3.1.3 → 3.3.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.
File without changes
File without changes
File without changes
package/package.json CHANGED
@@ -1,51 +1,48 @@
1
1
  {
2
2
  "name": "@graphql-inspector/validate-command",
3
- "version": "3.1.3",
3
+ "version": "3.3.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.3.0",
11
+ "@graphql-inspector/core": "3.3.0",
12
+ "@graphql-inspector/logger": "3.3.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
- "repository": {
35
- "type": "git",
36
- "url": "kamilkisiela/graphql-inspector",
37
- "directory": "packages/commands/validate"
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.3",
44
- "@graphql-inspector/core": "3.1.3",
45
- "@graphql-inspector/logger": "3.1.3",
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
- "scripts": {
49
- "prepack": "bob prepack"
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.3",
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.3",
11
- "@graphql-inspector/core": "3.1.3",
12
- "@graphql-inspector/logger": "3.1.3",
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
- }