@graphql-inspector/diff-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/diff-command",
3
- "version": "3.1.3",
3
+ "version": "3.3.0",
4
4
  "description": "Compare GraphQL Schemas",
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/diff"
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/diff"
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 +0,0 @@
1
- module.exports = () => process.exit(2);
@@ -1 +0,0 @@
1
- module.exports = ({ changes }) => changes;
@@ -1,154 +0,0 @@
1
- import '@graphql-inspector/testing';
2
- import { mockCommand } from '@graphql-inspector/commands';
3
- import { mockLogger, unmockLogger } from '@graphql-inspector/logger';
4
- import yargs from 'yargs';
5
- import { buildSchema } from 'graphql';
6
- import { resolve } from 'path';
7
- import createCommand from '../src';
8
-
9
- const oldSchema = 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
- const newSchema = buildSchema(/* GraphQL */ `
22
- type Post {
23
- id: ID!
24
- title: String!
25
- createdAt: String!
26
- }
27
-
28
- type Query {
29
- post: Post!
30
- }
31
- `);
32
-
33
- const diff = createCommand({
34
- config: {
35
- commands: [],
36
- loaders: [],
37
- },
38
- loaders: {
39
- async loadSchema(pointer) {
40
- if (pointer.includes('old')) {
41
- return oldSchema;
42
- }
43
-
44
- return newSchema;
45
- },
46
- async loadDocuments() {
47
- throw new Error('Not implemented');
48
- },
49
- },
50
- });
51
-
52
- describe('diff', () => {
53
- let spyReporter: jest.SpyInstance;
54
- let spyProcessExit: jest.SpyInstance;
55
- let spyProcessCwd: jest.SpyInstance;
56
-
57
- beforeEach(() => {
58
- yargs.reset();
59
- spyProcessExit = jest.spyOn(process, 'exit');
60
- spyProcessExit.mockImplementation();
61
-
62
- spyProcessCwd = jest
63
- .spyOn(process, 'cwd')
64
- .mockImplementation(() => __dirname);
65
-
66
- spyReporter = jest.fn();
67
- mockLogger(spyReporter as any);
68
- });
69
-
70
- afterEach(() => {
71
- yargs.reset();
72
- unmockLogger();
73
- spyProcessExit.mockRestore();
74
- spyProcessCwd.mockRestore();
75
- spyReporter.mockRestore();
76
- });
77
-
78
- test('should load graphql file', async () => {
79
- await mockCommand(diff, 'diff old.graphql old.graphql');
80
-
81
- expect(spyReporter).toHaveBeenCalledNormalized('No changes detected');
82
- expect(spyReporter).not.toHaveBeenCalledNormalized(
83
- 'Detected the following changes',
84
- );
85
- });
86
-
87
- test('should load different schema from graphql file', async () => {
88
- await mockCommand(diff, 'diff old.graphql new.graphql');
89
-
90
- expect(spyReporter).not.toHaveBeenCalledWith('No changes detected');
91
- expect(spyReporter).toHaveBeenCalledNormalized(
92
- 'Detected the following changes (4) between schemas:',
93
- );
94
-
95
- expect(spyProcessExit).toHaveBeenCalledWith(1);
96
- });
97
-
98
- test('should load rule by name', async () => {
99
- await mockCommand(
100
- diff,
101
- 'diff old.graphql new.graphql --rule suppressRemovalOfDeprecatedField',
102
- );
103
-
104
- expect(spyReporter).not.toHaveBeenCalledNormalized('does not exist');
105
- });
106
-
107
- test('should load rules with local path from fs', async () => {
108
- await mockCommand(
109
- diff,
110
- 'diff old.graphql new.graphql --rule ./assets/rule.js',
111
- );
112
-
113
- expect(spyReporter).not.toHaveBeenCalledNormalized('does not exist');
114
- });
115
-
116
- test('should load rules with absolute path from fs', async () => {
117
- await mockCommand(
118
- diff,
119
- `diff old.graphql new.graphql --rule ${resolve(
120
- __dirname,
121
- 'assets/rule.js',
122
- )}`,
123
- );
124
-
125
- expect(spyReporter).not.toHaveBeenCalledNormalized('does not exist');
126
- });
127
-
128
- test('should render error if file does not exist', async () => {
129
- await mockCommand(diff, `diff old.graphql new.graphql --rule noop.js`);
130
-
131
- expect(spyReporter).toHaveBeenCalledNormalized('does not exist');
132
- });
133
-
134
- test('should render error if file does not exist', async () => {
135
- await mockCommand(
136
- diff,
137
- `diff old.graphql new.graphql --onComplete ${resolve(
138
- __dirname,
139
- 'assets/onComplete.js',
140
- )}`,
141
- );
142
-
143
- expect(spyProcessExit).toHaveBeenCalledWith(2);
144
- });
145
-
146
- test('should render error if file does not exist', async () => {
147
- await mockCommand(
148
- diff,
149
- `diff old.graphql new.graphql --onComplete noop.js`,
150
- );
151
-
152
- expect(spyReporter).toHaveBeenCalledNormalized('does not exist');
153
- });
154
- });
package/dist/package.json DELETED
@@ -1,48 +0,0 @@
1
- {
2
- "name": "@graphql-inspector/diff-command",
3
- "version": "3.1.3",
4
- "description": "Compare GraphQL Schemas",
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/diff"
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,283 +0,0 @@
1
- import {
2
- CommandFactory,
3
- createCommand,
4
- ensureAbsolute,
5
- GlobalArgs,
6
- parseGlobalArgs,
7
- } from '@graphql-inspector/commands';
8
- import {
9
- Change,
10
- CompletionArgs,
11
- CompletionHandler,
12
- UsageHandler,
13
- CriticalityLevel,
14
- diff as diffSchema,
15
- DiffRule,
16
- Rule,
17
- } from '@graphql-inspector/core';
18
- import { bolderize, Logger, symbols } from '@graphql-inspector/logger';
19
- import { existsSync } from 'fs';
20
- import { GraphQLSchema } from 'graphql';
21
-
22
- export { CommandFactory };
23
-
24
- export async function handler(input: {
25
- oldSchema: GraphQLSchema;
26
- newSchema: GraphQLSchema;
27
- onComplete?: string;
28
- onUsage?: string;
29
- rules?: Array<string | number>;
30
- }) {
31
- const onComplete = input.onComplete
32
- ? resolveCompletionHandler(input.onComplete)
33
- : failOnBreakingChanges;
34
-
35
- const rules = input.rules
36
- ? input.rules
37
- .filter(isString)
38
- .map((name): Rule => {
39
- const rule = resolveRule(name);
40
-
41
- if (!rule) {
42
- throw new Error(`\Rule '${name}' does not exist!\n`);
43
- }
44
-
45
- return rule;
46
- })
47
- .filter((f) => f)
48
- : [];
49
-
50
- const changes = await diffSchema(input.oldSchema, input.newSchema, rules, {
51
- checkUsage: input.onUsage ? resolveUsageHandler(input.onUsage) : undefined,
52
- });
53
-
54
- if (changes.length === 0) {
55
- Logger.success('No changes detected');
56
- return;
57
- }
58
-
59
- Logger.log(
60
- `\nDetected the following changes (${changes.length}) between schemas:\n`,
61
- );
62
-
63
- const breakingChanges = changes.filter(
64
- (change) => change.criticality.level === CriticalityLevel.Breaking,
65
- );
66
- const dangerousChanges = changes.filter(
67
- (change) => change.criticality.level === CriticalityLevel.Dangerous,
68
- );
69
- const nonBreakingChanges = changes.filter(
70
- (change) => change.criticality.level === CriticalityLevel.NonBreaking,
71
- );
72
-
73
- if (breakingChanges.length) {
74
- reportBreakingChanges(breakingChanges);
75
- }
76
-
77
- if (dangerousChanges.length) {
78
- reportDangerousChanges(dangerousChanges);
79
- }
80
-
81
- if (nonBreakingChanges.length) {
82
- reportNonBreakingChanges(nonBreakingChanges);
83
- }
84
-
85
- onComplete({ breakingChanges, dangerousChanges, nonBreakingChanges });
86
- }
87
-
88
- export default createCommand<
89
- {},
90
- {
91
- oldSchema: string;
92
- newSchema: string;
93
- rule?: Array<string | number>;
94
- onComplete?: string;
95
- onUsage?: string;
96
- } & GlobalArgs
97
- >((api) => {
98
- const { loaders } = api;
99
-
100
- return {
101
- command: 'diff <oldSchema> <newSchema>',
102
- describe: 'Compare two GraphQL Schemas',
103
- builder(yargs) {
104
- return yargs
105
- .positional('oldSchema', {
106
- describe: 'Point to an old schema',
107
- type: 'string',
108
- demandOption: true,
109
- })
110
- .positional('newSchema', {
111
- describe: 'Point to a new schema',
112
- type: 'string',
113
- demandOption: true,
114
- })
115
- .options({
116
- rule: {
117
- describe: 'Add rules',
118
- array: true,
119
- },
120
- onComplete: {
121
- describe: 'Handle Completion',
122
- type: 'string',
123
- },
124
- onUsage: {
125
- describe: 'Checks usage of schema',
126
- type: 'string',
127
- },
128
- });
129
- },
130
- async handler(args) {
131
- try {
132
- const oldSchemaPointer = args.oldSchema;
133
- const newSchemaPointer = args.newSchema;
134
- const apolloFederation = args.federation || false;
135
- const aws = args.aws || false;
136
- const method = args.method?.toUpperCase() || 'POST';
137
- const { headers, leftHeaders, rightHeaders, token } =
138
- parseGlobalArgs(args);
139
-
140
- const oldSchemaHeaders = {
141
- ...(headers ?? {}),
142
- ...(leftHeaders ?? {}),
143
- };
144
- const newSchemaHeaders = {
145
- ...(headers ?? {}),
146
- ...(rightHeaders ?? {}),
147
- };
148
-
149
- const oldSchema = await loaders.loadSchema(
150
- oldSchemaPointer,
151
- {
152
- headers: oldSchemaHeaders,
153
- token,
154
- method,
155
- },
156
- apolloFederation,
157
- aws,
158
- );
159
- const newSchema = await loaders.loadSchema(
160
- newSchemaPointer,
161
- {
162
- headers: newSchemaHeaders,
163
- token,
164
- method,
165
- },
166
- apolloFederation,
167
- aws,
168
- );
169
-
170
- await handler({
171
- oldSchema,
172
- newSchema,
173
- rules: args.rule,
174
- onComplete: args.onComplete,
175
- onUsage: args.onUsage,
176
- });
177
- } catch (error) {
178
- Logger.error(error);
179
- throw error;
180
- }
181
- },
182
- };
183
- });
184
-
185
- function sortChanges(changes: Change[]) {
186
- return changes.slice().sort((a, b) => {
187
- const aPath = a.path || '';
188
- const bPath = b.path || '';
189
-
190
- if (aPath > bPath) {
191
- return 1;
192
- }
193
-
194
- if (bPath > aPath) {
195
- return -1;
196
- }
197
-
198
- return 0;
199
- });
200
- }
201
-
202
- function reportBreakingChanges(changes: Change[]) {
203
- const label = symbols.error;
204
- const sorted = sortChanges(changes);
205
-
206
- sorted.forEach((change) => {
207
- Logger.log(`${label} ${bolderize(change.message)}`);
208
- });
209
- }
210
-
211
- function reportDangerousChanges(changes: Change[]) {
212
- const label = symbols.warning;
213
- const sorted = sortChanges(changes);
214
-
215
- sorted.forEach((change) => {
216
- Logger.log(`${label} ${bolderize(change.message)}`);
217
- });
218
- }
219
-
220
- function reportNonBreakingChanges(changes: Change[]) {
221
- const label = symbols.success;
222
- const sorted = sortChanges(changes);
223
-
224
- sorted.forEach((change) => {
225
- Logger.log(`${label} ${bolderize(change.message)}`);
226
- });
227
- }
228
-
229
- function resolveRule(name: string): Rule | undefined {
230
- const filepath = ensureAbsolute(name);
231
- if (existsSync(filepath)) {
232
- return require(filepath);
233
- }
234
-
235
- return DiffRule[name as keyof typeof DiffRule];
236
- }
237
-
238
- function resolveCompletionHandler(name: string): CompletionHandler | never {
239
- const filepath = ensureAbsolute(name);
240
-
241
- try {
242
- require.resolve(filepath);
243
- } catch (error) {
244
- throw new Error(`CompletionHandler '${name}' does not exist!`);
245
- }
246
-
247
- const mod = require(filepath);
248
-
249
- return mod?.default || mod;
250
- }
251
-
252
- function resolveUsageHandler(name: string): UsageHandler | never {
253
- const filepath = ensureAbsolute(name);
254
-
255
- try {
256
- require.resolve(filepath);
257
- } catch (error) {
258
- throw new Error(`UsageHandler '${name}' does not exist!`);
259
- }
260
-
261
- const mod = require(filepath);
262
-
263
- return mod?.default || mod;
264
- }
265
-
266
- function failOnBreakingChanges({ breakingChanges }: CompletionArgs) {
267
- const breakingCount = breakingChanges.length;
268
-
269
- if (breakingCount) {
270
- Logger.error(
271
- `Detected ${breakingCount} breaking change${
272
- breakingCount > 1 ? 's' : ''
273
- }`,
274
- );
275
- process.exit(1);
276
- } else {
277
- Logger.success('No breaking changes detected');
278
- }
279
- }
280
-
281
- function isString(val: any): val is string {
282
- return typeof val === 'string';
283
- }