UNPKG

10.1 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.sortPrependValues = exports.codegen = void 0;
4const plugin_helpers_1 = require("@graphql-codegen/plugin-helpers");
5const schema_1 = require("@graphql-tools/schema");
6const utils_1 = require("@graphql-tools/utils");
7const graphql_1 = require("graphql");
8const execute_plugin_js_1 = require("./execute-plugin.js");
9const utils_js_1 = require("./utils.js");
10const transform_document_js_1 = require("./transform-document.js");
11async function codegen(options) {
12 const documents = options.documents || [];
13 const profiler = options.profiler ?? (0, plugin_helpers_1.createNoopProfiler)();
14 const skipDocumentsValidation = (0, utils_js_1.getSkipDocumentsValidationOption)(options);
15 if (documents.length > 0 && (0, utils_js_1.shouldValidateDuplicateDocuments)(skipDocumentsValidation)) {
16 await profiler.run(async () => validateDuplicateDocuments(documents), 'validateDuplicateDocuments');
17 }
18 const pluginPackages = Object.keys(options.pluginMap).map(key => options.pluginMap[key]);
19 // merged schema with parts added by plugins
20 const additionalTypeDefs = [];
21 for (const plugin of pluginPackages) {
22 const addToSchema = typeof plugin.addToSchema === 'function' ? plugin.addToSchema(options.config) : plugin.addToSchema;
23 if (addToSchema) {
24 additionalTypeDefs.push(addToSchema);
25 }
26 }
27 const federationInConfig = (0, utils_js_1.pickFlag)('federation', options.config);
28 const isFederation = (0, utils_js_1.prioritize)(federationInConfig, false);
29 if (isFederation && !(0, utils_js_1.hasFederationSpec)(options.schemaAst || options.schema)) {
30 additionalTypeDefs.push(plugin_helpers_1.federationSpec);
31 }
32 // Use mergeSchemas, only if there is no GraphQLSchema provided or the schema should be extended
33 const mergeNeeded = !options.schemaAst || additionalTypeDefs.length > 0;
34 const schemaInstance = await profiler.run(async () => {
35 return mergeNeeded
36 ? (0, schema_1.mergeSchemas)({
37 // If GraphQLSchema provided, use it
38 schemas: options.schemaAst ? [options.schemaAst] : [],
39 // If GraphQLSchema isn't provided but DocumentNode is, use it to get the final GraphQLSchema
40 typeDefs: options.schemaAst ? additionalTypeDefs : [options.schema, ...additionalTypeDefs],
41 convertExtensions: true,
42 assumeValid: true,
43 assumeValidSDL: true,
44 ...options.config,
45 })
46 : options.schemaAst;
47 }, 'Create schema instance');
48 const schemaDocumentNode = mergeNeeded || !options.schema ? (0, plugin_helpers_1.getCachedDocumentNodeFromSchema)(schemaInstance) : options.schema;
49 const documentTransforms = Array.isArray(options.documentTransforms) ? options.documentTransforms : [];
50 const transformedDocuments = await (0, transform_document_js_1.transformDocuments)({
51 ...options,
52 documentTransforms,
53 schema: schemaDocumentNode,
54 schemaAst: schemaInstance,
55 profiler,
56 });
57 if (schemaInstance &&
58 transformedDocuments.length > 0 &&
59 (0, utils_js_1.shouldValidateDocumentsAgainstSchema)(skipDocumentsValidation)) {
60 const ignored = ['NoUnusedFragments', 'NoUnusedVariables', 'KnownDirectives'];
61 if (typeof skipDocumentsValidation === 'object' && skipDocumentsValidation.ignoreRules) {
62 ignored.push(...(0, utils_1.asArray)(skipDocumentsValidation.ignoreRules));
63 }
64 const extraFragments = (0, utils_js_1.pickFlag)('externalFragments', options.config) || [];
65 const errors = await profiler.run(() => {
66 const fragments = extraFragments.map(f => ({
67 location: f.importFrom,
68 document: { kind: graphql_1.Kind.DOCUMENT, definitions: [f.node] },
69 }));
70 const rules = graphql_1.specifiedRules.filter(rule => !ignored.some(ignoredRule => rule.name.startsWith(ignoredRule)));
71 const schemaHash = (0, utils_js_1.extractHashFromSchema)(schemaInstance);
72 if (!schemaHash || !options.cache || transformedDocuments.some(d => typeof d.hash !== 'string')) {
73 return Promise.resolve((0, utils_1.validateGraphQlDocuments)(schemaInstance, [...transformedDocuments.flatMap(d => d.document), ...fragments.flatMap(f => f.document)], rules));
74 }
75 const cacheKey = [schemaHash]
76 .concat(transformedDocuments.map(doc => doc.hash))
77 .concat(JSON.stringify(fragments))
78 .join(',');
79 return options.cache('documents-validation', cacheKey, () => Promise.resolve((0, utils_1.validateGraphQlDocuments)(schemaInstance, [...transformedDocuments.flatMap(d => d.document), ...fragments.flatMap(f => f.document)], rules)));
80 }, 'Validate documents against schema');
81 if (errors.length > 0) {
82 throw new Error(`GraphQL Document Validation failed with ${errors.length} errors;
83 ${errors.map((error, index) => `Error ${index}: ${error.stack}`).join('\n\n')}`);
84 }
85 }
86 const prepend = new Set();
87 const append = new Set();
88 const output = await Promise.all(options.plugins.map(async (plugin) => {
89 const name = Object.keys(plugin)[0];
90 const pluginPackage = options.pluginMap[name];
91 const pluginConfig = plugin[name] || {};
92 const execConfig = typeof pluginConfig === 'object' ? { ...options.config, ...pluginConfig } : pluginConfig;
93 const result = await profiler.run(() => (0, execute_plugin_js_1.executePlugin)({
94 name,
95 config: execConfig,
96 parentConfig: options.config,
97 schema: schemaDocumentNode,
98 schemaAst: schemaInstance,
99 documents: transformedDocuments,
100 outputFilename: options.filename,
101 allPlugins: options.plugins,
102 skipDocumentsValidation: options.skipDocumentsValidation,
103 pluginContext: options.pluginContext,
104 profiler,
105 }, pluginPackage), `Plugin ${name}`);
106 if (typeof result === 'string') {
107 return result || '';
108 }
109 if ((0, plugin_helpers_1.isComplexPluginOutput)(result)) {
110 if (result.append && result.append.length > 0) {
111 for (const item of result.append) {
112 if (item) {
113 append.add(item);
114 }
115 }
116 }
117 if (result.prepend && result.prepend.length > 0) {
118 for (const item of result.prepend) {
119 if (item) {
120 prepend.add(item);
121 }
122 }
123 }
124 return result.content || '';
125 }
126 return '';
127 }));
128 return [...sortPrependValues(Array.from(prepend.values())), ...output, ...Array.from(append.values())]
129 .filter(Boolean)
130 .join('\n');
131}
132exports.codegen = codegen;
133function resolveCompareValue(a) {
134 if (a.startsWith('/*') || a.startsWith('//') || a.startsWith(' *') || a.startsWith(' */') || a.startsWith('*/')) {
135 return 0;
136 }
137 if (a.startsWith('package')) {
138 return 1;
139 }
140 if (a.startsWith('import')) {
141 return 2;
142 }
143 return 3;
144}
145function sortPrependValues(values) {
146 return values.sort((a, b) => {
147 const aV = resolveCompareValue(a);
148 const bV = resolveCompareValue(b);
149 if (aV < bV) {
150 return -1;
151 }
152 if (aV > bV) {
153 return 1;
154 }
155 return 0;
156 });
157}
158exports.sortPrependValues = sortPrependValues;
159function validateDuplicateDocuments(files) {
160 // duplicated names
161 const definitionMap = {};
162 function addDefinition(file, node, deduplicatedDefinitions) {
163 if (typeof node.name !== 'undefined') {
164 definitionMap[node.kind] ||= {};
165 definitionMap[node.kind][node.name.value] ||= {
166 paths: new Set(),
167 contents: new Set(),
168 };
169 const definitionKindMap = definitionMap[node.kind];
170 const length = definitionKindMap[node.name.value].contents.size;
171 definitionKindMap[node.name.value].paths.add(file.location);
172 definitionKindMap[node.name.value].contents.add((0, graphql_1.print)(node));
173 if (length === definitionKindMap[node.name.value].contents.size) {
174 return null;
175 }
176 }
177 return deduplicatedDefinitions.add(node);
178 }
179 for (const file of files) {
180 const deduplicatedDefinitions = new Set();
181 (0, graphql_1.visit)(file.document, {
182 OperationDefinition(node) {
183 addDefinition(file, node, deduplicatedDefinitions);
184 },
185 FragmentDefinition(node) {
186 addDefinition(file, node, deduplicatedDefinitions);
187 },
188 });
189 file.document.definitions = Array.from(deduplicatedDefinitions);
190 }
191 const kinds = Object.keys(definitionMap);
192 for (const kind of kinds) {
193 const definitionKindMap = definitionMap[kind];
194 const names = Object.keys(definitionKindMap);
195 if (names.length) {
196 const duplicated = names.filter(name => definitionKindMap[name].contents.size > 1);
197 if (!duplicated.length) {
198 continue;
199 }
200 const list = duplicated
201 .map(name => `
202 * ${name} found in:
203 ${[...definitionKindMap[name].paths]
204 .map(filepath => {
205 return `
206 - ${filepath}
207 `.trimEnd();
208 })
209 .join('')}
210 `.trimEnd())
211 .join('');
212 const definitionKindName = kind.replace('Definition', '').toLowerCase();
213 throw new Error(`Not all ${definitionKindName}s have an unique name: ${duplicated.join(', ')}: \n
214 ${list}
215 `);
216 }
217 }
218}