@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
JavaScript
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;
;