Files
30-seconds-of-code/node_modules/gatsby/dist/schema/print.js
2019-08-20 15:52:05 +02:00

350 lines
10 KiB
JavaScript

"use strict";
const fs = require(`fs-extra`);
const {
EnumTypeComposer,
InputTypeComposer,
InterfaceTypeComposer,
ObjectTypeComposer,
ScalarTypeComposer,
UnionTypeComposer
} = require(`graphql-compose`);
const report = require(`gatsby-cli/lib/reporter`);
const {
internalExtensionNames
} = require(`./extensions`);
const printTypeDefinitions = ({
config,
schemaComposer
}) => {
if (!config) return Promise.resolve();
const {
path,
include = {},
exclude = {},
withFieldTypes
} = config || {};
if (!path) {
report.error(`Printing type definitions aborted. Please provide a file path.`);
return Promise.resolve();
}
if (fs.existsSync(path)) {
report.error(`Printing type definitions aborted. The file \`${path}\` already exists.`);
return Promise.resolve();
}
const internalTypes = [`Boolean`, `Buffer`, `Date`, `Float`, `ID`, `Int`, `Internal`, `InternalInput`, `JSON`, `Json`, `Node`, `NodeInput`, `Query`, `String`];
const internalPlugins = [`internal-data-bridge`];
const typesToExclude = exclude.types || [];
const pluginsToExclude = exclude.plugins || [];
const getName = tc => tc.name || tc.getTypeName();
const isInternalType = tc => {
const typeName = getName(tc);
if (internalTypes.includes(typeName)) {
return true;
}
const plugin = tc.getExtension(`plugin`);
if (internalPlugins.includes(plugin)) {
return true;
}
return false;
};
const shouldIncludeType = tc => {
const typeName = getName(tc);
if (typesToExclude.includes(typeName)) {
return false;
}
if (include.types && !include.types.includes(typeName)) {
return false;
}
const plugin = tc.getExtension(`plugin`);
if (pluginsToExclude.includes(plugin)) {
return false;
}
if (include.plugins && !include.plugins.includes(plugin)) {
return false;
}
return true;
}; // Save processed type names, not references to the type composers,
// because of how graphql-compose, at least in v6, processes
// inline types
const processedTypes = new Set();
const typeDefs = new Set();
const addType = tc => {
const typeName = getName(tc);
if (!processedTypes.has(typeName) && !isInternalType(tc)) {
processedTypes.add(typeName);
return typeDefs.add(tc);
}
processedTypes.add(typeName);
return null;
};
const addWithFieldTypes = tc => {
if (addType(tc) && (tc instanceof ObjectTypeComposer || tc instanceof InterfaceTypeComposer || tc instanceof InputTypeComposer)) {
if (tc instanceof ObjectTypeComposer) {
const interfaces = tc.getInterfaces();
interfaces.forEach(iface => {
const ifaceName = getName(iface);
if (ifaceName !== `Node`) {
addWithFieldTypes(schemaComposer.getAnyTC(ifaceName));
}
});
}
tc.getFieldNames().forEach(fieldName => {
const fieldType = tc.getFieldTC(fieldName);
addWithFieldTypes(fieldType);
if (!(tc instanceof InputTypeComposer)) {
const fieldArgs = tc.getFieldArgs(fieldName);
Object.keys(fieldArgs).forEach(argName => {
addWithFieldTypes(tc.getFieldArgTC(fieldName, argName));
});
}
});
}
};
schemaComposer.forEach(tc => {
if (!isInternalType(tc) && shouldIncludeType(tc)) {
if (withFieldTypes) {
addWithFieldTypes(tc);
} else {
addType(tc);
}
}
});
const printedTypeDefs = [`### Type definitions saved at ${new Date().toISOString()} ###`];
try {
typeDefs.forEach(tc => printedTypeDefs.push(printType(tc)));
report.info(`Writing GraphQL type definitions to ${path}`);
return fs.writeFile(path, printedTypeDefs.join(`\n\n`));
} catch (error) {
report.error(`Failed writing type definitions to \`${path}\`.`, error);
return Promise.resolve();
}
};
const printType = (tc, typeName) => {
if (tc instanceof ObjectTypeComposer) {
return printObjectType(tc);
} else if (tc instanceof InterfaceTypeComposer) {
return printInterfaceType(tc);
} else if (tc instanceof UnionTypeComposer) {
return printUnionType(tc);
} else if (tc instanceof EnumTypeComposer) {
return printEnumType(tc);
} else if (tc instanceof ScalarTypeComposer) {
return printScalarType(tc);
} else if (tc instanceof InputTypeComposer) {
return printInputObjectType(tc);
} else {
throw new Error(`Did not recognize type of ${typeName}.`);
}
}; // ------------------------- graphql-js schemaPrinter -------------------------
const {
astFromValue,
print,
GraphQLString,
DEFAULT_DEPRECATION_REASON
} = require(`graphql`);
const {
printBlockString
} = require(`graphql/language/blockString`);
const {
flatMap
} = require(`graphql/polyfills/flatMap`);
const printScalarType = tc => {
const type = tc.getType();
return printDescription(type) + `scalar ${type.name}`;
};
const printObjectType = tc => {
const type = tc.getType();
const interfaces = type.getInterfaces();
const implementedInterfaces = interfaces.length ? ` implements ` + interfaces.map(i => i.name).join(` & `) : ``;
const extensions = tc.getExtensions();
if (tc.hasInterface(`Node`)) {
extensions.dontInfer = null;
}
const directives = tc.schemaComposer.getDirectives();
const printedDirectives = printDirectives(extensions, directives);
const fields = tc.hasInterface(`Node`) ? Object.values(type.getFields()).filter(field => ![`id`, `parent`, `children`, `internal`].includes(field.name)) : Object.values(type.getFields());
return printDescription(type) + `type ${type.name}${implementedInterfaces}${printedDirectives}` + printFields(fields, directives);
};
const printInterfaceType = tc => {
const type = tc.getType();
const extensions = tc.getExtensions();
const directives = tc.schemaComposer.getDirectives();
const printedDirectives = printDirectives(extensions, directives);
return printDescription(type) + `interface ${type.name}${printedDirectives}` + printFields(Object.values(type.getFields()), directives);
};
const printUnionType = tc => {
const type = tc.getType();
const types = type.getTypes();
const possibleTypes = types.length ? ` = ` + types.join(` | `) : ``;
return printDescription(type) + `union ` + type.name + possibleTypes;
};
const printEnumType = tc => {
const type = tc.getType();
const values = type.getValues().map((value, i) => printDescription(value, ` `, !i) + ` ` + value.name + printDeprecated(value));
return printDescription(type) + `enum ${type.name}` + printBlock(values);
};
const printInputObjectType = tc => {
const type = tc.getType();
const fields = Object.values(type.getFields()).map((f, i) => printDescription(f, ` `, !i) + ` ` + printInputValue(f));
return printDescription(type) + `input ${type.name}` + printBlock(fields);
};
const printFields = (fields, directives) => {
const printedFields = fields.map((f, i) => printDescription(f, ` `, !i) + ` ` + f.name + printArgs(f.args, ` `) + `: ` + String(f.type) + printDirectives(f.extensions || {}, directives) + printDeprecated(f));
return printBlock(printedFields);
};
const printBlock = items => items.length !== 0 ? ` {\n` + items.join(`\n`) + `\n}` : ``;
const printArgs = (args, indentation = ``) => {
if (args.length === 0) {
return ``;
} // If all args have no description, print them on one line
if (args.every(arg => !arg.description)) {
return `(` + args.map(printInputValue).join(`, `) + `)`;
}
return `(\n` + args.map((arg, i) => printDescription(arg, ` ` + indentation, !i) + ` ` + indentation + printInputValue(arg)).join(`\n`) + `\n` + indentation + `)`;
};
const printInputValue = arg => {
const defaultAST = astFromValue(arg.defaultValue, arg.type);
let argDecl = arg.name + `: ` + String(arg.type);
if (defaultAST) {
argDecl += ` = ${print(defaultAST)}`;
}
return argDecl;
};
const printDirectives = (extensions, directives) => Object.entries(extensions).map(([name, args]) => {
if ([...internalExtensionNames, `deprecated`].includes(name)) return ``;
return ` @${name}` + printDirectiveArgs(args, directives.find(directive => directive.name === name));
}).join(``);
const printDirectiveArgs = (args, directive) => {
if (!args || !directive) {
return ``;
}
const directiveArgs = Object.entries(args);
if (directiveArgs.length === 0) {
return ``;
}
return `(` + directiveArgs.map(([name, value]) => {
const arg = directive.args && directive.args.find(arg => arg.name === name);
return arg && `${name}: ${print(astFromValue(value, arg.type))}`;
}).join(`, `) + `)`;
};
const printDeprecated = fieldOrEnumVal => {
if (!fieldOrEnumVal.isDeprecated) {
return ``;
}
const reason = fieldOrEnumVal.deprecationReason;
const reasonAST = astFromValue(reason, GraphQLString);
if (reasonAST && reason !== `` && reason !== DEFAULT_DEPRECATION_REASON) {
return ` @deprecated(reason: ` + print(reasonAST) + `)`;
}
return ` @deprecated`;
};
const printDescription = (def, indentation = ``, firstInBlock = true) => {
if (!def.description) {
return ``;
}
const lines = descriptionLines(def.description, 120 - indentation.length);
const text = lines.join(`\n`);
const preferMultipleLines = text.length > 70;
const blockString = printBlockString(text, ``, preferMultipleLines);
const prefix = indentation && !firstInBlock ? `\n` + indentation : indentation;
return prefix + blockString.replace(/\n/g, `\n` + indentation) + `\n`;
};
const descriptionLines = (description, maxLen) => {
const rawLines = description.split(`\n`);
return flatMap(rawLines, line => {
if (line.length < maxLen + 5) {
return line;
} // For > 120 character long lines, cut at space boundaries into sublines
// of ~80 chars.
return breakLine(line, maxLen);
});
};
const breakLine = (line, maxLen) => {
const parts = line.split(new RegExp(`((?: |^).{15,${maxLen - 40}}(?= |$))`));
if (parts.length < 4) {
return [line];
}
const sublines = [parts[0] + parts[1] + parts[2]];
for (let i = 3; i < parts.length; i += 2) {
sublines.push(parts[i].slice(1) + parts[i + 1]);
}
return sublines;
};
module.exports = {
printTypeDefinitions
};
//# sourceMappingURL=print.js.map