Skip to content

Commit 10ab58d

Browse files
authored
CODEGEN-334 - Improve error logging (#10333)
1 parent c8a7fef commit 10ab58d

File tree

7 files changed

+136
-18
lines changed

7 files changed

+136
-18
lines changed

Diff for: .changeset/wicked-icons-mate.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphql-codegen/cli': patch
3+
---
4+
5+
Improve syntax error messages whilst loading schema/document

Diff for: .prettierignore

+4
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,7 @@ website/src/components/live-demo/LiveDemo.tsx
3030
# This should be added bc our rust test setup for the SWC plugin does a string diff, and it fails
3131
# bc it compares imports with double quotes against the formatted perttier single quotes
3232
packages/presets/swc-plugin/tests/fixtures
33+
34+
# Ignore intentional error files
35+
packages/graphql-codegen-cli/tests/test-files/schema-dir/error-schema.graphql
36+
packages/graphql-codegen-cli/tests/test-files/error-document.graphql

Diff for: packages/graphql-codegen-cli/src/codegen.ts

+2
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,8 @@ export async function executeCodegen(input: CodegenContext | Types.Config): Prom
412412
rendererOptions: {
413413
clearOutput: false,
414414
collapse: true,
415+
formatOutput: 'wrap',
416+
removeEmptyLines: false,
415417
},
416418
renderer: config.verbose ? 'verbose' : 'default',
417419
ctx: { errors: [] },

Diff for: packages/graphql-codegen-cli/src/load.ts

+22-18
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
} from '@graphql-tools/load';
1414
import { PrismaLoader } from '@graphql-tools/prisma-loader';
1515
import { UrlLoader } from '@graphql-tools/url-loader';
16-
import { GraphQLSchema } from 'graphql';
16+
import { GraphQLError, GraphQLSchema } from 'graphql';
1717

1818
export const defaultSchemaLoadOptions = {
1919
assumeValidSDL: true,
@@ -52,22 +52,17 @@ export async function loadSchema(
5252
return schema;
5353
} catch (e) {
5454
throw new Error(
55-
`
56-
Failed to load schema from ${Object.keys(schemaPointers).join(',')}:
57-
58-
${e.message || e}
59-
${e.stack || ''}
60-
61-
GraphQL Code Generator supports:
62-
- ES Modules and CommonJS exports (export as default or named export "schema")
63-
- Introspection JSON File
64-
- URL of GraphQL endpoint
65-
- Multiple files with type definitions (glob expression)
66-
- String in config file
67-
68-
Try to use one of above options and run codegen again.
69-
70-
`
55+
[
56+
`Failed to load schema from ${Object.keys(schemaPointers).join(',')}:`,
57+
printError(e),
58+
'\nGraphQL Code Generator supports:',
59+
'\n- ES Modules and CommonJS exports (export as default or named export "schema")',
60+
'- Introspection JSON File',
61+
'- URL of GraphQL endpoint',
62+
'- Multiple files with type definitions (glob expression)',
63+
'- String in config file',
64+
'\nTry to use one of above options and run codegen again.\n',
65+
].join('\n')
7166
);
7267
}
7368
}
@@ -107,6 +102,15 @@ export async function loadDocuments(
107102
return loadedFromToolkit;
108103
} catch (error) {
109104
if (config.ignoreNoDocuments) return [];
110-
throw error;
105+
throw new Error(
106+
[`Failed to load documents from ${Object.keys(documentPointers).join(',')}:`, printError(error)].join('\n')
107+
);
111108
}
112109
}
110+
111+
const printError = (error: any) => {
112+
if (error instanceof GraphQLError) {
113+
return String(error);
114+
}
115+
return [String(error.message || error), String(error.stack)].join('\n');
116+
};

Diff for: packages/graphql-codegen-cli/tests/generate-and-save.spec.ts

+99
Original file line numberDiff line numberDiff line change
@@ -254,4 +254,103 @@ describe('generate-and-save', () => {
254254
// makes sure it doesn't write a new file
255255
expect(writeSpy).toHaveBeenCalled();
256256
});
257+
258+
describe('Syntax errors when loading pointers', () => {
259+
const originalConsole = { ...console };
260+
const originalNodeEnv = process.env.NODE_ENV;
261+
262+
let consoleErrorMock;
263+
264+
beforeEach(() => {
265+
// Mock common console functions to avoid noise in the terminal
266+
global.console.log = jest.fn();
267+
global.console.warn = jest.fn();
268+
global.console.error = jest.fn();
269+
270+
// By default, the NODE_ENV is set to 'test', and this is used to silent console errors.
271+
// For these tests below, we want to see what's being logged out to console errors.
272+
process.env.NODE_ENV = 'not_test_so_error';
273+
274+
consoleErrorMock = jest.mocked(global.console.error);
275+
});
276+
277+
afterEach(() => {
278+
global.console = originalConsole;
279+
process.env.NODE_ENV = originalNodeEnv;
280+
});
281+
282+
test('Schema syntax error - should print native GraphQLError for', async () => {
283+
try {
284+
await generate(
285+
{
286+
verbose: true,
287+
schema: './tests/test-files/schema-dir/error-schema.graphql',
288+
generates: {
289+
'src/test.ts': {
290+
plugins: ['typescript'],
291+
},
292+
},
293+
},
294+
false
295+
);
296+
} catch {
297+
expect(consoleErrorMock.mock.calls[0][0]).toBeSimilarStringTo(
298+
'[FAILED] Failed to load schema from ./tests/test-files/schema-dir/error-schema.graphql:'
299+
);
300+
expect(consoleErrorMock.mock.calls[0][0]).toBeSimilarStringTo(
301+
'[FAILED] Syntax Error: Expected Name, found "!".'
302+
);
303+
// We can only use partial file path to the error file, because the error contains absolute path on the host machine
304+
expect(consoleErrorMock.mock.calls[0][0]).toBeSimilarStringTo(
305+
'/tests/test-files/schema-dir/error-schema.graphql:2:15'
306+
);
307+
expect(consoleErrorMock.mock.calls[0][0]).toBeSimilarStringTo(`
308+
[FAILED] 1 | type Query {
309+
[FAILED] 2 | foo: String!!
310+
[FAILED] | ^
311+
[FAILED] 3 | }
312+
[FAILED]
313+
[FAILED] GraphQL Code Generator supports:
314+
[FAILED]
315+
[FAILED] - ES Modules and CommonJS exports (export as default or named export "schema")
316+
[FAILED] - Introspection JSON File
317+
[FAILED] - URL of GraphQL endpoint
318+
[FAILED] - Multiple files with type definitions (glob expression)
319+
[FAILED] - String in config file
320+
[FAILED]
321+
[FAILED] Try to use one of above options and run codegen again.
322+
`);
323+
}
324+
});
325+
326+
test('Document syntax error - should print native GraphQLError', async () => {
327+
try {
328+
await generate(
329+
{
330+
verbose: true,
331+
schema: './tests/test-files/schema-dir/schema.ts',
332+
documents: './tests/test-files/error-document.graphql',
333+
generates: {
334+
'src/test.ts': {
335+
plugins: ['typescript'],
336+
},
337+
},
338+
},
339+
false
340+
);
341+
} catch {
342+
expect(consoleErrorMock.mock.calls[0][0]).toBeSimilarStringTo(
343+
'Failed to load documents from ./tests/test-files/error-document.graphql:'
344+
);
345+
expect(consoleErrorMock.mock.calls[0][0]).toBeSimilarStringTo('Syntax Error: Expected "{", found <EOF>.');
346+
// We can only use partial file path to the error file, because the error contains absolute path on the host machine
347+
expect(consoleErrorMock.mock.calls[0][0]).toBeSimilarStringTo('/tests/test-files/error-document.graphql:2:1');
348+
expect(consoleErrorMock.mock.calls[0][0]).toBeSimilarStringTo(`
349+
[FAILED] 1 | query
350+
[FAILED] 2 |
351+
[FAILED] | ^
352+
`);
353+
}
354+
});
355+
});
257356
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
query
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
type Query {
2+
foo: String!!
3+
}

0 commit comments

Comments
 (0)