Skip to content

Commit 4ac5762

Browse files
authored
CODEGEN-834 - [cli] Handle partial generation success (#10376)
* Add writeOnPartialSuccess flag to partially write successful generateiong * Update config name from writeOnPartialSuccess to allowPartialOutputs * Ensure consistent experience on complete failure, update tests * Restructure code and comment * Update website schema * Update doc
1 parent 2efa946 commit 4ac5762

File tree

12 files changed

+668
-417
lines changed

12 files changed

+668
-417
lines changed

.changeset/many-pets-attend.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@graphql-codegen/plugin-helpers': minor
3+
'@graphql-codegen/cli': major
4+
---
5+
6+
Add `allowPartialOutputs` flag to partially write successful generation to files

packages/graphql-codegen-cli/src/codegen.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ function createCache(): <T>(namespace: string, key: string, factory: () => Promi
6969
};
7070
}
7171

72-
export async function executeCodegen(input: CodegenContext | Types.Config): Promise<Types.FileOutput[]> {
72+
export async function executeCodegen(
73+
input: CodegenContext | Types.Config
74+
): Promise<{ result: Types.FileOutput[]; error: Error | null }> {
7375
const context = ensureContext(input);
7476
const config = context.getConfig();
7577
const pluginContext = context.getPluginContext();
@@ -431,13 +433,13 @@ export async function executeCodegen(input: CodegenContext | Types.Config): Prom
431433
printLogs();
432434
}
433435

436+
let error: Error | null = null;
434437
if (executedContext.errors.length > 0) {
435438
const errors = executedContext.errors.map(subErr => subErr.message || subErr.toString());
436-
const newErr = new AggregateError(executedContext.errors, String(errors.join('\n\n')));
439+
error = new AggregateError(executedContext.errors, String(errors.join('\n\n')));
437440
// Best-effort to all stack traces for debugging
438-
newErr.stack = `${newErr.stack}\n\n${executedContext.errors.map(subErr => subErr.stack).join('\n\n')}`;
439-
throw newErr;
441+
error.stack = `${error.stack}\n\n${executedContext.errors.map(subErr => subErr.stack).join('\n\n')}`;
440442
}
441443

442-
return result;
444+
return { result, error };
443445
}

packages/graphql-codegen-cli/src/generate-and-save.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { createHash } from 'crypto';
22
import { dirname, isAbsolute, join } from 'path';
3+
import logSymbols from 'log-symbols';
34
import { Types } from '@graphql-codegen/plugin-helpers';
45
import { executeCodegen } from './codegen.js';
56
import { CodegenContext, ensureContext } from './config.js';
67
import { lifecycleHooks } from './hooks.js';
78
import { debugLog } from './utils/debugging.js';
89
import { mkdirp, readFile, unlinkFile, writeFile } from './utils/file-system.js';
910
import { createWatcher } from './utils/watcher.js';
11+
import { getLogger } from './utils/logger.js';
1012

1113
const hash = (content: string): string => createHash('sha1').update(content).digest('base64');
1214

@@ -133,7 +135,27 @@ export async function generate(
133135
return createWatcher(context, writeOutput).runningWatcher;
134136
}
135137

136-
const outputFiles = await context.profiler.run(() => executeCodegen(context), 'executeCodegen');
138+
const { result: outputFiles, error } = await context.profiler.run(() => executeCodegen(context), 'executeCodegen');
139+
140+
if (error) {
141+
// If all generation failed, just throw to return non-zero code.
142+
if (outputFiles.length === 0) {
143+
throw error;
144+
}
145+
146+
// If partial success, but partial output is not allowed, throw to return non-zero code.
147+
if (!config.allowPartialOutputs) {
148+
getLogger().error(
149+
` ${logSymbols.error} One or more errors occurred, no files were generated. To allow output on errors, set config.allowPartialOutputs=true`
150+
);
151+
throw error;
152+
}
153+
154+
// If partial success, and partial output is allowed, warn and proceed to write to files.
155+
getLogger().warn(
156+
` ${logSymbols.warning} One or more errors occurred, some files were generated. To prevent any output on errors, set config.allowPartialOutputs=false`
157+
);
158+
}
137159

138160
await context.profiler.run(() => writeOutput(outputFiles), 'writeOutput');
139161
await context.profiler.run(() => lifecycleHooks(config.hooks).beforeDone(), 'Lifecycle: beforeDone');

packages/graphql-codegen-cli/src/utils/watcher.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,10 @@ export const createWatcher = (
7878
const debouncedExec = debounce(() => {
7979
if (!isShutdown) {
8080
executeCodegen(initialContext)
81-
.then(onNext, () => Promise.resolve())
81+
.then(
82+
({ result }) => onNext(result),
83+
() => Promise.resolve()
84+
)
8285
.then(() => emitWatching(watchDirectory));
8386
}
8487
}, 100);
@@ -198,7 +201,10 @@ export const createWatcher = (
198201
*/
199202
stopWatching.runningWatcher = new Promise<void>((resolve, reject) => {
200203
executeCodegen(initialContext)
201-
.then(onNext, () => Promise.resolve())
204+
.then(
205+
({ result }) => onNext(result),
206+
() => Promise.resolve()
207+
)
202208
.then(() => runWatcher(abortController.signal))
203209
.catch(err => {
204210
watcherSubscription.unsubscribe();

0 commit comments

Comments
 (0)