Skip to content

Commit c88a311

Browse files
committed
documentsReadOnly implemented
1 parent d3261ac commit c88a311

File tree

4 files changed

+224
-1
lines changed

4 files changed

+224
-1
lines changed

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export async function executeCodegen(
8282
let rootConfig: { [key: string]: any } = {};
8383
let rootSchemas: Types.Schema[];
8484
let rootDocuments: Types.OperationDocument[];
85+
let rootDocumentsReadOnly: Types.OperationDocument[];
8586
const generates: { [filename: string]: Types.ConfiguredOutput } = {};
8687

8788
const cache = createCache();
@@ -132,6 +133,9 @@ export async function executeCodegen(
132133
/* Normalize root "documents" field */
133134
rootDocuments = normalizeInstanceOrArray<Types.OperationDocument>(config.documents);
134135

136+
/* Normalize root "documentsReadOnly" field */
137+
rootDocumentsReadOnly = normalizeInstanceOrArray<Types.OperationDocument>(config.documentsReadOnly);
138+
135139
/* Normalize "generators" field */
136140
const generateKeys = Object.keys(config.generates || {});
137141

@@ -225,8 +229,12 @@ export async function executeCodegen(
225229
let outputSchema: DocumentNode;
226230
const outputFileTemplateConfig = outputConfig.config || {};
227231
let outputDocuments: Types.DocumentFile[] = [];
232+
let outputDocumentsReadOnly: Types.DocumentFile[] = [];
228233
const outputSpecificSchemas = normalizeInstanceOrArray<Types.Schema>(outputConfig.schema);
229234
let outputSpecificDocuments = normalizeInstanceOrArray<Types.OperationDocument>(outputConfig.documents);
235+
const outputSpecificDocumentsReadOnly = normalizeInstanceOrArray<Types.OperationDocument>(
236+
outputConfig.documentsReadOnly
237+
);
230238

231239
const preset: Types.OutputPreset | null = hasPreset
232240
? typeof outputConfig.preset === 'string'
@@ -321,6 +329,37 @@ export async function executeCodegen(
321329
});
322330

323331
outputDocuments = result.documents;
332+
333+
const allReadOnlyDenormalizedPointers = [
334+
...rootDocumentsReadOnly,
335+
...outputSpecificDocumentsReadOnly,
336+
];
337+
338+
if (allReadOnlyDenormalizedPointers.length > 0) {
339+
const readOnlyPointerMap: any = {};
340+
for (const denormalizedPtr of allReadOnlyDenormalizedPointers) {
341+
if (typeof denormalizedPtr === 'string') {
342+
readOnlyPointerMap[denormalizedPtr] = {};
343+
} else if (typeof denormalizedPtr === 'object') {
344+
Object.assign(readOnlyPointerMap, denormalizedPtr);
345+
}
346+
}
347+
348+
const readOnlyHash = JSON.stringify(readOnlyPointerMap);
349+
const readOnlyResult = await cache('documents-read-only', readOnlyHash, async () => {
350+
try {
351+
const documents = await context.loadDocuments(readOnlyPointerMap);
352+
return { documents };
353+
} catch (error) {
354+
if (error instanceof NoTypeDefinitionsFound && config.ignoreNoDocuments) {
355+
return { documents: [] };
356+
}
357+
throw error;
358+
}
359+
});
360+
361+
outputDocumentsReadOnly = readOnlyResult.documents;
362+
}
324363
},
325364
filename,
326365
`Load GraphQL documents: ${filename}`,
@@ -391,6 +430,7 @@ export async function executeCodegen(
391430
schema: outputSchema,
392431
schemaAst: outputSchemaAst,
393432
documents: outputDocuments,
433+
documentsReadOnly: outputDocumentsReadOnly,
394434
config: mergedConfig,
395435
pluginMap,
396436
pluginContext,
@@ -406,6 +446,7 @@ export async function executeCodegen(
406446
schema: outputSchema,
407447
schemaAst: outputSchemaAst,
408448
documents: outputDocuments,
449+
documentsReadOnly: outputDocumentsReadOnly,
409450
config: mergedConfig,
410451
pluginMap,
411452
pluginContext,

packages/graphql-codegen-cli/tests/codegen.spec.ts

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1442,6 +1442,167 @@ describe('Codegen Executor', () => {
14421442
});
14431443
});
14441444

1445+
describe('documentsReadOnly', () => {
1446+
it('should pass documentsReadOnly to preset buildGeneratesSection', async () => {
1447+
let capturedDocumentsReadOnly: Types.DocumentFile[] | undefined;
1448+
1449+
const capturePreset: Types.OutputPreset = {
1450+
buildGeneratesSection: options => {
1451+
capturedDocumentsReadOnly = options.documentsReadOnly;
1452+
return [
1453+
{
1454+
filename: 'out1/result.ts',
1455+
pluginMap: { typescript: require('@graphql-codegen/typescript') },
1456+
plugins: [{ typescript: {} }],
1457+
schema: options.schema,
1458+
documents: options.documents,
1459+
config: options.config,
1460+
},
1461+
];
1462+
},
1463+
};
1464+
1465+
await executeCodegen({
1466+
schema: SIMPLE_TEST_SCHEMA,
1467+
documents: `query root { f }`,
1468+
documentsReadOnly: `fragment Frag on MyType { f }`,
1469+
generates: {
1470+
'out1/': { preset: capturePreset },
1471+
},
1472+
});
1473+
1474+
expect(capturedDocumentsReadOnly).toBeDefined();
1475+
expect(capturedDocumentsReadOnly).toHaveLength(1);
1476+
});
1477+
1478+
it('should not include documentsReadOnly content in regular documents', async () => {
1479+
let capturedDocuments: Types.DocumentFile[] | undefined;
1480+
let capturedDocumentsReadOnly: Types.DocumentFile[] | undefined;
1481+
1482+
const capturePreset: Types.OutputPreset = {
1483+
buildGeneratesSection: options => {
1484+
capturedDocuments = options.documents;
1485+
capturedDocumentsReadOnly = options.documentsReadOnly;
1486+
return [
1487+
{
1488+
filename: 'out1/result.ts',
1489+
pluginMap: { typescript: require('@graphql-codegen/typescript') },
1490+
plugins: [{ typescript: {} }],
1491+
schema: options.schema,
1492+
documents: options.documents,
1493+
config: options.config,
1494+
},
1495+
];
1496+
},
1497+
};
1498+
1499+
await executeCodegen({
1500+
schema: SIMPLE_TEST_SCHEMA,
1501+
documents: `query root { f }`,
1502+
documentsReadOnly: `query readOnlyQuery { f }`,
1503+
generates: {
1504+
'out1/': { preset: capturePreset },
1505+
},
1506+
});
1507+
1508+
expect(capturedDocuments).toHaveLength(1);
1509+
expect(capturedDocumentsReadOnly).toHaveLength(1);
1510+
1511+
const documentNames = capturedDocuments.flatMap(
1512+
d => d.document?.definitions.map((def: any) => def.name?.value) ?? []
1513+
);
1514+
const readOnlyNames = capturedDocumentsReadOnly.flatMap(
1515+
d => d.document?.definitions.map((def: any) => def.name?.value) ?? []
1516+
);
1517+
1518+
expect(documentNames).toContain('root');
1519+
expect(documentNames).not.toContain('readOnlyQuery');
1520+
expect(readOnlyNames).toContain('readOnlyQuery');
1521+
expect(readOnlyNames).not.toContain('root');
1522+
});
1523+
1524+
it('should not include documentsReadOnly operations in non-preset plugin output', async () => {
1525+
const { result } = await executeCodegen({
1526+
schema: SIMPLE_TEST_SCHEMA,
1527+
documents: `query root { f }`,
1528+
documentsReadOnly: `query readOnlyQuery { f }`,
1529+
generates: {
1530+
'out1.ts': { plugins: ['typescript-operations'] },
1531+
},
1532+
});
1533+
1534+
expect(result).toHaveLength(1);
1535+
// Only the regular document operation should be generated
1536+
expect(result[0].content).toContain('RootQuery');
1537+
expect(result[0].content).not.toContain('ReadOnlyQuery');
1538+
});
1539+
1540+
it('should support output-level documentsReadOnly', async () => {
1541+
let capturedDocumentsReadOnly: Types.DocumentFile[] | undefined;
1542+
1543+
const capturePreset: Types.OutputPreset = {
1544+
buildGeneratesSection: options => {
1545+
capturedDocumentsReadOnly = options.documentsReadOnly;
1546+
return [
1547+
{
1548+
filename: 'out1/result.ts',
1549+
pluginMap: { typescript: require('@graphql-codegen/typescript') },
1550+
plugins: [{ typescript: {} }],
1551+
schema: options.schema,
1552+
documents: options.documents,
1553+
config: options.config,
1554+
},
1555+
];
1556+
},
1557+
};
1558+
1559+
await executeCodegen({
1560+
schema: SIMPLE_TEST_SCHEMA,
1561+
generates: {
1562+
'out1/': {
1563+
preset: capturePreset,
1564+
documentsReadOnly: `fragment Frag on MyType { f }`,
1565+
},
1566+
},
1567+
});
1568+
1569+
expect(capturedDocumentsReadOnly).toHaveLength(1);
1570+
});
1571+
1572+
it('should merge root and output-level documentsReadOnly', async () => {
1573+
let capturedDocumentsReadOnly: Types.DocumentFile[] | undefined;
1574+
1575+
const capturePreset: Types.OutputPreset = {
1576+
buildGeneratesSection: options => {
1577+
capturedDocumentsReadOnly = options.documentsReadOnly;
1578+
return [
1579+
{
1580+
filename: 'out1/result.ts',
1581+
pluginMap: { typescript: require('@graphql-codegen/typescript') },
1582+
plugins: [{ typescript: {} }],
1583+
schema: options.schema,
1584+
documents: options.documents,
1585+
config: options.config,
1586+
},
1587+
];
1588+
},
1589+
};
1590+
1591+
await executeCodegen({
1592+
schema: SIMPLE_TEST_SCHEMA,
1593+
documentsReadOnly: `fragment RootFrag on MyType { f }`,
1594+
generates: {
1595+
'out1/': {
1596+
preset: capturePreset,
1597+
documentsReadOnly: `fragment OutputFrag on MyType { f }`,
1598+
},
1599+
},
1600+
});
1601+
1602+
expect(capturedDocumentsReadOnly).toHaveLength(2);
1603+
});
1604+
});
1605+
14451606
it('should not run out of memory when generating very complex types (issue #7720)', async () => {
14461607
const { result } = await executeCodegen({
14471608
schema: ['../../dev-test/gatsby/schema.graphql'],

packages/utils/plugins-helpers/src/types.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ export namespace Types {
1111
schema: DocumentNode;
1212
schemaAst?: GraphQLSchema;
1313
documents: Types.DocumentFile[];
14+
/**
15+
* @description Documents that are loaded for reading (e.g. to resolve fragment types) but will not have type files generated for them.
16+
*/
17+
documentsReadOnly?: Types.DocumentFile[];
1418
config: { [key: string]: any };
1519
pluginMap: {
1620
[name: string]: CodegenPlugin;
@@ -303,6 +307,12 @@ export namespace Types {
303307
* For more details: https://graphql-code-generator.com/docs/config-reference/documents-field
304308
*/
305309
documents?: InstanceOrArray<OperationDocument>;
310+
/**
311+
* @description A pointer(s) to your GraphQL documents that will be read but will not have type files generated for them.
312+
* These documents are available to plugins for type resolution (e.g. fragment types), but no output files will be generated based on them.
313+
* Accepts the same formats as `documents`.
314+
*/
315+
documentsReadOnly?: InstanceOrArray<OperationDocument>;
306316
/**
307317
* @description A pointer(s) to your GraphQL schema. This schema will be available only for this specific `generates` record.
308318
* You can use one of the following:
@@ -358,6 +368,10 @@ export namespace Types {
358368
schema: DocumentNode;
359369
schemaAst?: GraphQLSchema;
360370
documents: Types.DocumentFile[];
371+
/**
372+
* @description Documents that are loaded for reading (e.g. to resolve fragment types) but will not have type files generated for them.
373+
*/
374+
documentsReadOnly: Types.DocumentFile[];
361375
config: PluginConfig;
362376
pluginMap: {
363377
[name: string]: CodegenPlugin;
@@ -434,6 +448,12 @@ export namespace Types {
434448
* For more details: https://graphql-code-generator.com/docs/config-reference/documents-field
435449
*/
436450
documents?: InstanceOrArray<OperationDocument>;
451+
/**
452+
* @description A pointer(s) to your GraphQL documents that will be read but will not have type files generated for them.
453+
* These documents are available to plugins for type resolution (e.g. fragment types), but no output files will be generated based on them.
454+
* Accepts the same formats as `documents`.
455+
*/
456+
documentsReadOnly?: InstanceOrArray<OperationDocument>;
437457
/**
438458
* @type object
439459
* @additionalProperties true

website/src/components/live-demo/generate.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ export async function generate(config: string, schema: string, documents?: strin
6767
baseOutputDir: filename,
6868
presetConfig: { ...outputOptions.presetConfig, typesPath: 'graphql.ts', baseTypesPath: 'graphql.ts' },
6969
...props,
70-
}))
70+
documentsReadOnly: [],
71+
} as any))
7172
);
7273
}
7374
}

0 commit comments

Comments
 (0)