UNPKG

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