@graphql-mesh/compose-cli
Version:
222 lines (221 loc) • 10.1 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.getComposedSchemaFromConfig = getComposedSchemaFromConfig;
const graphql_1 = require("graphql");
const fusion_composition_1 = require("@graphql-mesh/fusion-composition");
const utils_1 = require("@graphql-mesh/utils");
const code_file_loader_1 = require("@graphql-tools/code-file-loader");
const graphql_file_loader_1 = require("@graphql-tools/graphql-file-loader");
const load_1 = require("@graphql-tools/load");
const schema_1 = require("@graphql-tools/schema");
const utils_2 = require("@graphql-tools/utils");
const fetch_1 = require("@whatwg-node/fetch");
const isDebug = ['1', 'y', 'yes', 't', 'true'].includes(String(process.env.DEBUG));
async function getComposedSchemaFromConfig(config, logger) {
const ctx = {
fetch: config.fetch || fetch_1.fetch,
cwd: config.cwd || globalThis.process?.cwd?.(),
logger,
};
const subgraphConfigsForComposition = await Promise.all(config.subgraphs.map(async (subgraphCLIConfig) => {
const { name: subgraphName, schema$ } = subgraphCLIConfig.sourceHandler(ctx);
const log = logger.child({ subgraph: subgraphName });
log.debug(`Loading subgraph`);
let subgraphSchema;
try {
subgraphSchema = await schema$;
}
catch (e) {
if (isDebug) {
log.error(`Failed to load subgraph`, e);
}
else {
log.error(e.message || e);
log.error(`Failed to load subgraph`);
}
process.exit(1);
}
return {
name: subgraphName,
schema: subgraphSchema,
transforms: subgraphCLIConfig.transforms,
};
}));
let additionalTypeDefs;
if (config.additionalTypeDefs != null) {
let additionalFieldDirectiveUsed = false;
if (typeof config.additionalTypeDefs === 'string' && config.additionalTypeDefs?.includes(' ')) {
try {
additionalTypeDefs = [(0, utils_1.parseWithCache)(config.additionalTypeDefs)];
}
catch (e) {
logger.error(`Failed to parse additional type definitions: ${e.message || e}`);
process.exit(1);
}
}
else {
const result = await (0, load_1.loadTypedefs)(config.additionalTypeDefs, {
noLocation: true,
assumeValid: true,
assumeValidSDL: true,
loaders: [new code_file_loader_1.CodeFileLoader(), new graphql_file_loader_1.GraphQLFileLoader()],
});
additionalTypeDefs = result.map(source => source.document ||
(0, utils_1.parseWithCache)(source.rawSDL || (0, utils_2.printSchemaWithDirectives)(source.schema)));
}
additionalTypeDefs = additionalTypeDefs.map(doc => (0, graphql_1.visit)(doc, {
[graphql_1.Kind.FIELD_DEFINITION](node) {
additionalFieldDirectiveUsed = true;
return {
...node,
directives: [
...(node.directives || []),
{
kind: graphql_1.Kind.DIRECTIVE,
name: { kind: graphql_1.Kind.NAME, value: 'additionalField' },
},
],
};
},
}));
if (additionalFieldDirectiveUsed) {
additionalTypeDefs.unshift((0, graphql_1.parse)(/* GraphQL */ `
directive @additionalField on FIELD_DEFINITION
`));
}
}
if (config.subgraph) {
const annotatedSubgraphs = (0, fusion_composition_1.getAnnotatedSubgraphs)(subgraphConfigsForComposition, {
ignoreSemanticConventions: config.ignoreSemanticConventions,
alwaysAddTransportDirective: true,
});
const subgraph = annotatedSubgraphs.find(sg => sg.name === config.subgraph);
if (!subgraph) {
logger.error(`Subgraph ${config.subgraph} not found`);
process.exit(1);
}
return (0, graphql_1.print)(subgraph.typeDefs);
}
const result = (0, fusion_composition_1.composeSubgraphs)(subgraphConfigsForComposition);
if (result.errors?.length) {
logger.error(`Failed to compose subgraphs`);
for (const error of result.errors) {
if (isDebug) {
logger.error(error);
}
else {
logger.error(error.message || error);
}
}
process.exit(1);
}
if (!result.supergraphSdl) {
logger.error(`Unknown error: Supergraph is empty`);
process.exit(1);
}
let composedSchema;
if (fusion_composition_1.futureAdditions.length) {
let futureAddition;
while ((futureAddition = fusion_composition_1.futureAdditions.pop())) {
additionalTypeDefs ||= [];
composedSchema ||= (0, graphql_1.buildSchema)(result.supergraphSdl, {
noLocation: true,
assumeValid: true,
assumeValidSDL: true,
});
const sourceType = composedSchema.getType(futureAddition.sourceTypeName);
if (!sourceType) {
logger.error(`Target type ${futureAddition.sourceTypeName} not found`);
process.exit(1);
}
const sourceField = sourceType.getFields()[futureAddition.sourceFieldName];
if (!sourceField) {
logger.error(`Target field ${futureAddition.sourceFieldName} not found`);
process.exit(1);
}
const sourceReturnType = (0, graphql_1.getNamedType)(sourceField.type);
if (!(0, graphql_1.isNamedType)(sourceReturnType)) {
logger.error(`Target field ${futureAddition.sourceFieldName} has no return type`);
process.exit(1);
}
const interfaceOrType = (0, graphql_1.isInterfaceType)(composedSchema.getType(futureAddition.targetTypeName))
? 'interface'
: 'type';
additionalTypeDefs.push((0, graphql_1.parse)(/* GraphQL */ `
extend ${interfaceOrType} ${futureAddition.targetTypeName} {
${futureAddition.targetFieldName}: ${sourceReturnType}
@additionalField
@resolveTo(
sourceTypeName: "${futureAddition.sourceTypeName}",
sourceFieldName: "${futureAddition.sourceFieldName}",
sourceName: "${futureAddition.sourceName}",
sourceArgs: ${(0, graphql_1.print)((0, utils_2.astFromValueUntyped)(futureAddition.sourceArgs))},
requiredSelectionSet: "${futureAddition.requiredSelectionSet}"
)
}
`));
}
}
if (additionalTypeDefs?.length /* TODO || config.transforms?.length */) {
composedSchema ||= (0, graphql_1.buildSchema)(result.supergraphSdl, {
noLocation: true,
assumeValid: true,
assumeValidSDL: true,
});
if (additionalTypeDefs?.length) {
const originalComposedSchema = composedSchema;
composedSchema = (0, schema_1.mergeSchemas)({
schemas: [composedSchema],
typeDefs: additionalTypeDefs,
assumeValid: true,
assumeValidSDL: true,
});
// Fix extra additionalField annotations
if (composedSchema.getDirective('additionalField')) {
let composedSchemaAST = (0, utils_2.getDocumentNodeFromSchema)(composedSchema);
composedSchemaAST = (0, graphql_1.visit)(composedSchemaAST, {
[graphql_1.Kind.FIELD_DEFINITION](node, __key, __parent, __path, ancestors) {
const directiveNames = node.directives?.map(directive => directive.name.value);
if (directiveNames.includes('additionalField')) {
if (directiveNames.includes('join__field') || directiveNames.includes('source')) {
return {
...node,
directives: node.directives?.filter(directive => directive.name.value !== 'additionalField'),
};
}
const typeInAncestor = [...ancestors]
.reverse()
.find(ancestor => !Array.isArray(ancestor) &&
ancestor != null &&
typeof ancestor === 'object' &&
'kind' in ancestor);
if (typeInAncestor) {
const typeInOriginalSchema = originalComposedSchema.getType(typeInAncestor.name.value);
if (typeInOriginalSchema && 'getFields' in typeInOriginalSchema) {
const field = typeInOriginalSchema.getFields()[node.name.value];
if (field) {
return {
...node,
directives: node.directives?.filter(directive => directive.name.value !== 'additionalField'),
};
}
}
}
}
},
});
return (0, graphql_1.print)(composedSchemaAST);
}
}
/* TODO
if (config.transforms?.length) {
logger.info('Applying transforms');
for (const transform of config.transforms) {
composedSchema = transform(composedSchema);
}
}
*/
return (0, utils_2.printSchemaWithDirectives)(composedSchema);
}
return result.supergraphSdl;
}
;