UNPKG

@graphql-codegen/cli

Version:

<p align="center"> <img src="https://github.com/dotansimha/graphql-code-generator/blob/master/logo.png?raw=true" /> </p>

334 lines (327 loc) • 17.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.executeCodegen = void 0; const tslib_1 = require("tslib"); const plugin_helpers_1 = require("@graphql-codegen/plugin-helpers"); const core_1 = require("@graphql-codegen/core"); const utils_1 = require("@graphql-tools/utils"); const graphql_1 = require("graphql"); const plugins_js_1 = require("./plugins.js"); const presets_js_1 = require("./presets.js"); const debugging_js_1 = require("./utils/debugging.js"); const config_js_1 = require("./config.js"); const fs_1 = tslib_1.__importDefault(require("fs")); const path_1 = tslib_1.__importDefault(require("path")); const os_1 = require("os"); const module_1 = require("module"); const listr2_1 = require("listr2"); /** * Poor mans ESM detection. * Looking at this and you have a better method? * Send a PR. */ const isESMModule = (typeof __dirname === 'string') === false; const makeDefaultLoader = (from) => { if (fs_1.default.statSync(from).isDirectory()) { from = path_1.default.join(from, '__fake.js'); } const relativeRequire = (0, module_1.createRequire)(from); return async (mod) => { return Promise.resolve().then(() => tslib_1.__importStar(require(isESMModule ? /** * For ESM we currently have no "resolve path" solution * as import.meta is unavailable in a CommonJS context * and furthermore unavailable in stable Node.js. **/ mod : relativeRequire.resolve(mod)))); }; }; function createCache() { const cache = new Map(); return function ensure(namespace, key, factory) { const cacheKey = `${namespace}:${key}`; const cachedValue = cache.get(cacheKey); if (cachedValue) { return cachedValue; } const value = factory(); cache.set(cacheKey, value); return value; }; } async function executeCodegen(input) { const context = (0, config_js_1.ensureContext)(input); const config = context.getConfig(); const pluginContext = context.getPluginContext(); const result = []; let rootConfig = {}; let rootSchemas; let rootDocuments; const generates = {}; const cache = createCache(); function wrapTask(task, source, taskName, ctx) { return () => { return context.profiler.run(async () => { try { await Promise.resolve().then(() => task()); } catch (error) { if (source && !(error instanceof graphql_1.GraphQLError)) { error.source = source; } ctx.errors.push(error); throw error; } }, taskName); }; } async function normalize() { /* Load Require extensions */ const requireExtensions = (0, plugin_helpers_1.normalizeInstanceOrArray)(config.require); const loader = makeDefaultLoader(context.cwd); for (const mod of requireExtensions) { await loader(mod); } /* Root plugin config */ rootConfig = config.config || {}; /* Normalize root "schema" field */ rootSchemas = (0, plugin_helpers_1.normalizeInstanceOrArray)(config.schema); /* Normalize root "documents" field */ rootDocuments = (0, plugin_helpers_1.normalizeInstanceOrArray)(config.documents); /* Normalize "generators" field */ const generateKeys = Object.keys(config.generates || {}); if (generateKeys.length === 0) { throw new plugin_helpers_1.DetailedError('Invalid Codegen Configuration!', ` Please make sure that your codegen config file contains the "generates" field, with a specification for the plugins you need. It should looks like that: schema: - my-schema.graphql generates: my-file.ts: - plugin1 - plugin2 - plugin3 `); } for (const filename of generateKeys) { const output = (generates[filename] = (0, plugin_helpers_1.normalizeOutputParam)(config.generates[filename])); if (!output.preset && (!output.plugins || output.plugins.length === 0)) { throw new plugin_helpers_1.DetailedError('Invalid Codegen Configuration!', ` Please make sure that your codegen config file has defined plugins list for output "${filename}". It should looks like that: schema: - my-schema.graphql generates: my-file.ts: - plugin1 - plugin2 - plugin3 `); } } if (rootSchemas.length === 0 && Object.keys(generates).some(filename => !generates[filename].schema || (Array.isArray(generates[filename].schema === 'object') && generates[filename].schema.length === 0))) { throw new plugin_helpers_1.DetailedError('Invalid Codegen Configuration!', ` Please make sure that your codegen config file contains either the "schema" field or every generated file has its own "schema" field. It should looks like that: schema: - my-schema.graphql or: generates: path/to/output: schema: my-schema.graphql `); } } const isTest = process.env.NODE_ENV === 'test'; const tasks = new listr2_1.Listr([ { title: 'Parse Configuration', task: () => normalize(), }, { title: 'Generate outputs', task: (ctx, task) => { const generateTasks = Object.keys(generates).map(filename => { const outputConfig = generates[filename]; const hasPreset = !!outputConfig.preset; const title = `Generate to ${filename}`; return { title, task: async (_, subTask) => { let outputSchemaAst; let outputSchema; const outputFileTemplateConfig = outputConfig.config || {}; let outputDocuments = []; const outputSpecificSchemas = (0, plugin_helpers_1.normalizeInstanceOrArray)(outputConfig.schema); let outputSpecificDocuments = (0, plugin_helpers_1.normalizeInstanceOrArray)(outputConfig.documents); const preset = hasPreset ? typeof outputConfig.preset === 'string' ? await (0, presets_js_1.getPresetByName)(outputConfig.preset, makeDefaultLoader(context.cwd)) : outputConfig.preset : null; if (preset) { if (preset.prepareDocuments) { outputSpecificDocuments = await preset.prepareDocuments(filename, outputSpecificDocuments); } } return subTask.newListr([ { title: 'Load GraphQL schemas', task: wrapTask(async () => { (0, debugging_js_1.debugLog)(`[CLI] Loading Schemas`); const schemaPointerMap = {}; const allSchemaDenormalizedPointers = [...rootSchemas, ...outputSpecificSchemas]; for (const denormalizedPtr of allSchemaDenormalizedPointers) { if (typeof denormalizedPtr === 'string') { schemaPointerMap[denormalizedPtr] = {}; } else if (typeof denormalizedPtr === 'object') { Object.assign(schemaPointerMap, denormalizedPtr); } } const hash = JSON.stringify(schemaPointerMap); const result = await cache('schema', hash, async () => { const outputSchemaAst = await context.loadSchema(schemaPointerMap); const outputSchema = (0, plugin_helpers_1.getCachedDocumentNodeFromSchema)(outputSchemaAst); return { outputSchemaAst, outputSchema, }; }); outputSchemaAst = result.outputSchemaAst; outputSchema = result.outputSchema; }, filename, `Load GraphQL schemas: ${filename}`, ctx), }, { title: 'Load GraphQL documents', task: wrapTask(async () => { (0, debugging_js_1.debugLog)(`[CLI] Loading Documents`); const documentPointerMap = {}; const allDocumentsDenormalizedPointers = [...rootDocuments, ...outputSpecificDocuments]; for (const denormalizedPtr of allDocumentsDenormalizedPointers) { if (typeof denormalizedPtr === 'string') { documentPointerMap[denormalizedPtr] = {}; } else if (typeof denormalizedPtr === 'object') { Object.assign(documentPointerMap, denormalizedPtr); } } const hash = JSON.stringify(documentPointerMap); const result = await cache('documents', hash, async () => { const documents = await context.loadDocuments(documentPointerMap); return { documents, }; }); outputDocuments = result.documents; }, filename, `Load GraphQL documents: ${filename}`, ctx), }, { title: 'Generate', task: wrapTask(async () => { (0, debugging_js_1.debugLog)(`[CLI] Generating output`); const normalizedPluginsArray = (0, plugin_helpers_1.normalizeConfig)(outputConfig.plugins); const pluginLoader = config.pluginLoader || makeDefaultLoader(context.cwd); const pluginPackages = await Promise.all(normalizedPluginsArray.map(plugin => (0, plugins_js_1.getPluginByName)(Object.keys(plugin)[0], pluginLoader))); const pluginMap = Object.fromEntries(pluginPackages.map((pkg, i) => { const plugin = normalizedPluginsArray[i]; const name = Object.keys(plugin)[0]; return [name, pkg]; })); const mergedConfig = { ...rootConfig, ...(typeof outputFileTemplateConfig === 'string' ? { value: outputFileTemplateConfig } : outputFileTemplateConfig), emitLegacyCommonJSImports: (0, config_js_1.shouldEmitLegacyCommonJSImports)(config, filename), }; const outputs = preset ? await context.profiler.run(async () => preset.buildGeneratesSection({ baseOutputDir: filename, presetConfig: outputConfig.presetConfig || {}, plugins: normalizedPluginsArray, schema: outputSchema, schemaAst: outputSchemaAst, documents: outputDocuments, config: mergedConfig, pluginMap, pluginContext, profiler: context.profiler, }), `Build Generates Section: ${filename}`) : [ { filename, plugins: normalizedPluginsArray, schema: outputSchema, schemaAst: outputSchemaAst, documents: outputDocuments, config: mergedConfig, pluginMap, pluginContext, profiler: context.profiler, }, ]; const process = async (outputArgs) => { const output = await (0, core_1.codegen)({ ...{ ...outputArgs, emitLegacyCommonJSImports: (0, config_js_1.shouldEmitLegacyCommonJSImports)(config, outputArgs.filename), }, cache, }); result.push({ filename: outputArgs.filename, content: output, hooks: outputConfig.hooks || {}, }); }; await context.profiler.run(() => Promise.all(outputs.map(process)), `Codegen: ${filename}`); }, filename, `Generate: ${filename}`, ctx), }, ], { // it stops when of the tasks failed exitOnError: true, }); }, // It doesn't stop when one of tasks failed, to finish at least some of outputs exitOnError: false, concurrent: (0, os_1.cpus)().length, }; }); return task.newListr(generateTasks); }, }, ], { rendererOptions: { clearOutput: false, collapse: true, }, renderer: config.verbose ? 'verbose' : 'default', ctx: { errors: [] }, rendererSilent: isTest || config.silent, exitOnError: true, }); // All the errors throw in `listr2` are collected in context // Running tasks doesn't throw anything const executedContext = await tasks.run(); if (config.debug) { // if we have debug logs, make sure to print them before throwing the errors (0, debugging_js_1.printLogs)(); } if (executedContext.errors.length > 0) { const errors = executedContext.errors.map(subErr => (0, plugin_helpers_1.isDetailedError)(subErr) ? `${subErr.message} for "${subErr.source}"${subErr.details}` : subErr.message || subErr.toString()); const newErr = new utils_1.AggregateError(executedContext.errors, `${errors.join('\n\n')}`); // Best-effort to all stack traces for debugging newErr.stack = `${newErr.stack}\n\n${executedContext.errors.map(subErr => subErr.stack).join('\n\n')}`; throw newErr; } return result; } exports.executeCodegen = executeCodegen;