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