UNPKG

17.6 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.executeCodegen = void 0;
4const tslib_1 = require("tslib");
5const plugin_helpers_1 = require("@graphql-codegen/plugin-helpers");
6const core_1 = require("@graphql-codegen/core");
7const utils_1 = require("@graphql-tools/utils");
8const graphql_1 = require("graphql");
9const plugins_js_1 = require("./plugins.js");
10const presets_js_1 = require("./presets.js");
11const debugging_js_1 = require("./utils/debugging.js");
12const config_js_1 = require("./config.js");
13const fs_1 = tslib_1.__importDefault(require("fs"));
14const path_1 = tslib_1.__importDefault(require("path"));
15const os_1 = require("os");
16const module_1 = require("module");
17const listr2_1 = require("listr2");
18/**
19 * Poor mans ESM detection.
20 * Looking at this and you have a better method?
21 * Send a PR.
22 */
23const isESMModule = (typeof __dirname === 'string') === false;
24const makeDefaultLoader = (from) => {
25 if (fs_1.default.statSync(from).isDirectory()) {
26 from = path_1.default.join(from, '__fake.js');
27 }
28 const relativeRequire = (0, module_1.createRequire)(from);
29 return async (mod) => {
30 return Promise.resolve().then(() => tslib_1.__importStar(require(isESMModule
31 ? /**
32 * For ESM we currently have no "resolve path" solution
33 * as import.meta is unavailable in a CommonJS context
34 * and furthermore unavailable in stable Node.js.
35 **/
36 mod
37 : relativeRequire.resolve(mod))));
38 };
39};
40function createCache() {
41 const cache = new Map();
42 return function ensure(namespace, key, factory) {
43 const cacheKey = `${namespace}:${key}`;
44 const cachedValue = cache.get(cacheKey);
45 if (cachedValue) {
46 return cachedValue;
47 }
48 const value = factory();
49 cache.set(cacheKey, value);
50 return value;
51 };
52}
53async function executeCodegen(input) {
54 const context = (0, config_js_1.ensureContext)(input);
55 const config = context.getConfig();
56 const pluginContext = context.getPluginContext();
57 const result = [];
58 let rootConfig = {};
59 let rootSchemas;
60 let rootDocuments;
61 const generates = {};
62 const cache = createCache();
63 function wrapTask(task, source, taskName, ctx) {
64 return () => {
65 return context.profiler.run(async () => {
66 try {
67 await Promise.resolve().then(() => task());
68 }
69 catch (error) {
70 if (source && !(error instanceof graphql_1.GraphQLError)) {
71 error.source = source;
72 }
73 ctx.errors.push(error);
74 throw error;
75 }
76 }, taskName);
77 };
78 }
79 async function normalize() {
80 /* Load Require extensions */
81 const requireExtensions = (0, plugin_helpers_1.normalizeInstanceOrArray)(config.require);
82 const loader = makeDefaultLoader(context.cwd);
83 for (const mod of requireExtensions) {
84 await loader(mod);
85 }
86 /* Root plugin config */
87 rootConfig = config.config || {};
88 /* Normalize root "schema" field */
89 rootSchemas = (0, plugin_helpers_1.normalizeInstanceOrArray)(config.schema);
90 /* Normalize root "documents" field */
91 rootDocuments = (0, plugin_helpers_1.normalizeInstanceOrArray)(config.documents);
92 /* Normalize "generators" field */
93 const generateKeys = Object.keys(config.generates || {});
94 if (generateKeys.length === 0) {
95 throw new plugin_helpers_1.DetailedError('Invalid Codegen Configuration!', `
96 Please make sure that your codegen config file contains the "generates" field, with a specification for the plugins you need.
97
98 It should looks like that:
99
100 schema:
101 - my-schema.graphql
102 generates:
103 my-file.ts:
104 - plugin1
105 - plugin2
106 - plugin3
107 `);
108 }
109 for (const filename of generateKeys) {
110 const output = (generates[filename] = (0, plugin_helpers_1.normalizeOutputParam)(config.generates[filename]));
111 if (!output.preset && (!output.plugins || output.plugins.length === 0)) {
112 throw new plugin_helpers_1.DetailedError('Invalid Codegen Configuration!', `
113 Please make sure that your codegen config file has defined plugins list for output "${filename}".
114
115 It should looks like that:
116
117 schema:
118 - my-schema.graphql
119 generates:
120 my-file.ts:
121 - plugin1
122 - plugin2
123 - plugin3
124 `);
125 }
126 }
127 if (rootSchemas.length === 0 &&
128 Object.keys(generates).some(filename => !generates[filename].schema ||
129 (Array.isArray(generates[filename].schema === 'object') &&
130 generates[filename].schema.length === 0))) {
131 throw new plugin_helpers_1.DetailedError('Invalid Codegen Configuration!', `
132 Please make sure that your codegen config file contains either the "schema" field
133 or every generated file has its own "schema" field.
134
135 It should looks like that:
136 schema:
137 - my-schema.graphql
138
139 or:
140 generates:
141 path/to/output:
142 schema: my-schema.graphql
143 `);
144 }
145 }
146 const isTest = process.env.NODE_ENV === 'test';
147 const tasks = new listr2_1.Listr([
148 {
149 title: 'Parse Configuration',
150 task: () => normalize(),
151 },
152 {
153 title: 'Generate outputs',
154 task: (ctx, task) => {
155 const generateTasks = Object.keys(generates).map(filename => {
156 const outputConfig = generates[filename];
157 const hasPreset = !!outputConfig.preset;
158 const title = `Generate to ${filename}`;
159 return {
160 title,
161 task: async (_, subTask) => {
162 let outputSchemaAst;
163 let outputSchema;
164 const outputFileTemplateConfig = outputConfig.config || {};
165 let outputDocuments = [];
166 const outputSpecificSchemas = (0, plugin_helpers_1.normalizeInstanceOrArray)(outputConfig.schema);
167 let outputSpecificDocuments = (0, plugin_helpers_1.normalizeInstanceOrArray)(outputConfig.documents);
168 const preset = hasPreset
169 ? typeof outputConfig.preset === 'string'
170 ? await (0, presets_js_1.getPresetByName)(outputConfig.preset, makeDefaultLoader(context.cwd))
171 : outputConfig.preset
172 : null;
173 if (preset) {
174 if (preset.prepareDocuments) {
175 outputSpecificDocuments = await preset.prepareDocuments(filename, outputSpecificDocuments);
176 }
177 }
178 return subTask.newListr([
179 {
180 title: 'Load GraphQL schemas',
181 task: wrapTask(async () => {
182 (0, debugging_js_1.debugLog)(`[CLI] Loading Schemas`);
183 const schemaPointerMap = {};
184 const allSchemaDenormalizedPointers = [...rootSchemas, ...outputSpecificSchemas];
185 for (const denormalizedPtr of allSchemaDenormalizedPointers) {
186 if (typeof denormalizedPtr === 'string') {
187 schemaPointerMap[denormalizedPtr] = {};
188 }
189 else if (typeof denormalizedPtr === 'object') {
190 Object.assign(schemaPointerMap, denormalizedPtr);
191 }
192 }
193 const hash = JSON.stringify(schemaPointerMap);
194 const result = await cache('schema', hash, async () => {
195 const outputSchemaAst = await context.loadSchema(schemaPointerMap);
196 const outputSchema = (0, plugin_helpers_1.getCachedDocumentNodeFromSchema)(outputSchemaAst);
197 return {
198 outputSchemaAst,
199 outputSchema,
200 };
201 });
202 outputSchemaAst = result.outputSchemaAst;
203 outputSchema = result.outputSchema;
204 }, filename, `Load GraphQL schemas: ${filename}`, ctx),
205 },
206 {
207 title: 'Load GraphQL documents',
208 task: wrapTask(async () => {
209 (0, debugging_js_1.debugLog)(`[CLI] Loading Documents`);
210 const documentPointerMap = {};
211 const allDocumentsDenormalizedPointers = [...rootDocuments, ...outputSpecificDocuments];
212 for (const denormalizedPtr of allDocumentsDenormalizedPointers) {
213 if (typeof denormalizedPtr === 'string') {
214 documentPointerMap[denormalizedPtr] = {};
215 }
216 else if (typeof denormalizedPtr === 'object') {
217 Object.assign(documentPointerMap, denormalizedPtr);
218 }
219 }
220 const hash = JSON.stringify(documentPointerMap);
221 const result = await cache('documents', hash, async () => {
222 const documents = await context.loadDocuments(documentPointerMap);
223 return {
224 documents,
225 };
226 });
227 outputDocuments = result.documents;
228 }, filename, `Load GraphQL documents: ${filename}`, ctx),
229 },
230 {
231 title: 'Generate',
232 task: wrapTask(async () => {
233 (0, debugging_js_1.debugLog)(`[CLI] Generating output`);
234 const normalizedPluginsArray = (0, plugin_helpers_1.normalizeConfig)(outputConfig.plugins);
235 const pluginLoader = config.pluginLoader || makeDefaultLoader(context.cwd);
236 const pluginPackages = await Promise.all(normalizedPluginsArray.map(plugin => (0, plugins_js_1.getPluginByName)(Object.keys(plugin)[0], pluginLoader)));
237 const pluginMap = Object.fromEntries(pluginPackages.map((pkg, i) => {
238 const plugin = normalizedPluginsArray[i];
239 const name = Object.keys(plugin)[0];
240 return [name, pkg];
241 }));
242 const mergedConfig = {
243 ...rootConfig,
244 ...(typeof outputFileTemplateConfig === 'string'
245 ? { value: outputFileTemplateConfig }
246 : outputFileTemplateConfig),
247 emitLegacyCommonJSImports: (0, config_js_1.shouldEmitLegacyCommonJSImports)(config, filename),
248 };
249 const outputs = preset
250 ? await context.profiler.run(async () => preset.buildGeneratesSection({
251 baseOutputDir: filename,
252 presetConfig: outputConfig.presetConfig || {},
253 plugins: normalizedPluginsArray,
254 schema: outputSchema,
255 schemaAst: outputSchemaAst,
256 documents: outputDocuments,
257 config: mergedConfig,
258 pluginMap,
259 pluginContext,
260 profiler: context.profiler,
261 }), `Build Generates Section: ${filename}`)
262 : [
263 {
264 filename,
265 plugins: normalizedPluginsArray,
266 schema: outputSchema,
267 schemaAst: outputSchemaAst,
268 documents: outputDocuments,
269 config: mergedConfig,
270 pluginMap,
271 pluginContext,
272 profiler: context.profiler,
273 },
274 ];
275 const process = async (outputArgs) => {
276 const output = await (0, core_1.codegen)({
277 ...{
278 ...outputArgs,
279 emitLegacyCommonJSImports: (0, config_js_1.shouldEmitLegacyCommonJSImports)(config, outputArgs.filename),
280 },
281 cache,
282 });
283 result.push({
284 filename: outputArgs.filename,
285 content: output,
286 hooks: outputConfig.hooks || {},
287 });
288 };
289 await context.profiler.run(() => Promise.all(outputs.map(process)), `Codegen: ${filename}`);
290 }, filename, `Generate: ${filename}`, ctx),
291 },
292 ], {
293 // it stops when of the tasks failed
294 exitOnError: true,
295 });
296 },
297 // It doesn't stop when one of tasks failed, to finish at least some of outputs
298 exitOnError: false,
299 concurrent: (0, os_1.cpus)().length,
300 };
301 });
302 return task.newListr(generateTasks);
303 },
304 },
305 ], {
306 rendererOptions: {
307 clearOutput: false,
308 collapse: true,
309 },
310 renderer: config.verbose ? 'verbose' : 'default',
311 ctx: { errors: [] },
312 rendererSilent: isTest || config.silent,
313 exitOnError: true,
314 });
315 // All the errors throw in `listr2` are collected in context
316 // Running tasks doesn't throw anything
317 const executedContext = await tasks.run();
318 if (config.debug) {
319 // if we have debug logs, make sure to print them before throwing the errors
320 (0, debugging_js_1.printLogs)();
321 }
322 if (executedContext.errors.length > 0) {
323 const errors = executedContext.errors.map(subErr => (0, plugin_helpers_1.isDetailedError)(subErr)
324 ? `${subErr.message} for "${subErr.source}"${subErr.details}`
325 : subErr.message || subErr.toString());
326 const newErr = new utils_1.AggregateError(executedContext.errors, `${errors.join('\n\n')}`);
327 // Best-effort to all stack traces for debugging
328 newErr.stack = `${newErr.stack}\n\n${executedContext.errors.map(subErr => subErr.stack).join('\n\n')}`;
329 throw newErr;
330 }
331 return result;
332}
333exports.executeCodegen = executeCodegen;