spectaql
Version:
A powerful library for autogenerating static GraphQL API documentation
385 lines (343 loc) • 12.1 kB
JavaScript
;Object.defineProperty(exports, "__esModule", { value: true });exports.addMetadataFromDirectables = exports.DEFAULT_DIRECTIVE_OPTION_NAME = exports.DEFAULT_DIRECTIVE_NAME = void 0;exports.generateDirectiveSdl = generateDirectiveSdl;exports.generateOptionsSdl = generateOptionsSdl;exports.generateSpectaqlDirectiveSupport = generateSpectaqlDirectiveSupport;exports.generateSpectaqlSdl = generateSpectaqlSdl;var _isEmpty = _interopRequireDefault(require("lodash/isEmpty"));
var _set = _interopRequireDefault(require("lodash/set"));
var _graphql = require("graphql");
var _utils = require("@graphql-tools/utils");
var _microfiber = require("microfiber");function _interopRequireDefault(e) {return e && e.__esModule ? e : { default: e };}
const DEFAULT_DIRECTIVE_NAME = exports.DEFAULT_DIRECTIVE_NAME = 'spectaql';
const DEFAULT_DIRECTIVE_OPTION_NAME = exports.DEFAULT_DIRECTIVE_OPTION_NAME = 'SpectaQLOption';
const MICROFIBER_OPTIONS = Object.freeze({
fixQueryAndMutationAndSubscriptionTypes: false,
removeUnusedTypes: false,
removeFieldsWithMissingTypes: false,
removeArgsWithMissingTypes: false,
removeInputFieldsWithMissingTypes: false,
removePossibleTypesOfMissingTypes: false,
cleanupSchemaImmediately: false
});
function parseExample(val) {
try {
return JSON.parse(val);
} catch {
return String(val);
}
}
const OPTION_TO_CONVERTER_FN = {
undocumented: (val) => val === true || val === 'true',
documented: (val) => val === true || val === 'true',
example: (val) => parseExample(val),
examples: (vals) => JSON.parse(vals)
};
function generateSpectaqlSdl({
directiveName = DEFAULT_DIRECTIVE_NAME,
optionsTypeName = DEFAULT_DIRECTIVE_OPTION_NAME
} = {}) {
return (
generateDirectiveSdl({ directiveName, optionsTypeName }) +
'\n' +
generateOptionsSdl({ optionsTypeName }) +
'\n');
}
function generateDirectiveSdl({
directiveName = DEFAULT_DIRECTIVE_NAME,
optionsTypeName = DEFAULT_DIRECTIVE_OPTION_NAME
} = {}) {
return `directive @${directiveName}(options: [${optionsTypeName}]) on ${Object.values(
_graphql.DirectiveLocation
).join(' | ')}`;
}
function generateOptionsSdl({
optionsTypeName = DEFAULT_DIRECTIVE_OPTION_NAME
} = {}) {
return `input ${optionsTypeName} { key: String!, value: String! }`;
}
function processDirective(directive) {
return ((directive === null || directive === void 0 ? void 0 : directive.options) || []).reduce((acc, { key: option, value }) => {
if (OPTION_TO_CONVERTER_FN[option]) {
acc[option] = OPTION_TO_CONVERTER_FN[option](value);
}
return acc;
}, {});
}
function generateSpectaqlDirectiveSupport({
options: spectaqlDirectiveOptions = {},
userSdl = ''
} = {}) {
const directables = [];
const {
onlyAddIfMissing,
directiveName = DEFAULT_DIRECTIVE_NAME,
optionsTypeName = DEFAULT_DIRECTIVE_OPTION_NAME
} = spectaqlDirectiveOptions;
let { directiveSdl, optionsSdl } = spectaqlDirectiveOptions;
if (
!directiveSdl && (
!userSdl.includes(`directive @${directiveName}`) || !onlyAddIfMissing))
{
directiveSdl = generateSpectaqlSdl(spectaqlDirectiveOptions);
}
if (
!optionsSdl && (
!userSdl.includes(`input ${optionsTypeName}`) || !onlyAddIfMissing))
{
optionsSdl = generateOptionsSdl(spectaqlDirectiveOptions);
}
function typeHandler(type, schema, mapperKind) {var _getDirective;
const directive = (_getDirective = (0, _utils.getDirective)(schema, type, 'spectaql')) === null || _getDirective === void 0 ? void 0 : _getDirective[0];
if (!(0, _isEmpty.default)(directive)) {
directables.push({
directive: processDirective(directive),
type,
mapperKind
});
}
}
function configHandler(config, fieldName, typeName, schema, mapperKind) {var _getDirective2;
const directive = (_getDirective2 = (0, _utils.getDirective)(schema, config, 'spectaql')) === null || _getDirective2 === void 0 ? void 0 : _getDirective2[0];
if (!(0, _isEmpty.default)(directive)) {
directables.push({
directive: processDirective(directive),
config,
fieldName,
typeName,
mapperKind
});
}
}
function enumValueHandler(
config,
typeName,
schema,
externalValue,
mapperKind)
{var _getDirective3;
const directive = (_getDirective3 = (0, _utils.getDirective)(schema, config, 'spectaql')) === null || _getDirective3 === void 0 ? void 0 : _getDirective3[0];
if (!(0, _isEmpty.default)(directive)) {
directables.push({
directive: processDirective(directive),
config,
typeName,
externalValue,
mapperKind
});
}
}
const HANDLER_MAP = {
[_utils.MapperKind.TYPE]: (...args) => typeHandler(...args, _utils.MapperKind.TYPE),
[_utils.MapperKind.SCALAR_TYPE]: (...args) =>
typeHandler(...args, _utils.MapperKind.SCALAR_TYPE),
[_utils.MapperKind.ENUM_TYPE]: (...args) =>
typeHandler(...args, _utils.MapperKind.ENUM_TYPE),
[_utils.MapperKind.COMPOSITE_TYPE]: (...args) =>
typeHandler(...args, _utils.MapperKind.COMPOSITE_TYPE),
[_utils.MapperKind.OBJECT_TYPE]: (...args) =>
typeHandler(...args, _utils.MapperKind.OBJECT_TYPE),
[_utils.MapperKind.INPUT_OBJECT_TYPE]: (...args) =>
typeHandler(...args, _utils.MapperKind.INPUT_OBJECT_TYPE),
[_utils.MapperKind.ABSTRACT_TYPE]: (...args) =>
typeHandler(...args, _utils.MapperKind.ABSTRACT_TYPE),
[_utils.MapperKind.UNION_TYPE]: (...args) =>
typeHandler(...args, _utils.MapperKind.UNION_TYPE),
[_utils.MapperKind.INTERFACE_TYPE]: (...args) =>
typeHandler(...args, _utils.MapperKind.INTERFACE_TYPE),
[_utils.MapperKind.ROOT_OBJECT]: (...args) =>
typeHandler(...args, _utils.MapperKind.ROOT_OBJECT),
[_utils.MapperKind.QUERY]: (...args) => typeHandler(...args, _utils.MapperKind.QUERY),
[_utils.MapperKind.MUTATION]: (...args) =>
typeHandler(...args, _utils.MapperKind.MUTATION),
[_utils.MapperKind.SUBSCRIPTION]: (...args) =>
typeHandler(...args, _utils.MapperKind.SUBSCRIPTION),
[_utils.MapperKind.ENUM_VALUE]: (...args) =>
enumValueHandler(...args, _utils.MapperKind.ENUM_VALUE),
[_utils.MapperKind.FIELD]: (...args) => configHandler(...args, _utils.MapperKind.FIELD),
[_utils.MapperKind.OBJECT_FIELD]: (...args) =>
configHandler(...args, _utils.MapperKind.OBJECT_FIELD),
[_utils.MapperKind.ROOT_FIELD]: (...args) =>
configHandler(...args, _utils.MapperKind.ROOT_FIELD),
[_utils.MapperKind.QUERY_ROOT_FIELD]: (...args) =>
configHandler(...args, _utils.MapperKind.QUERY_ROOT_FIELD),
[_utils.MapperKind.MUTATION_ROOT_FIELD]: (...args) =>
configHandler(...args, _utils.MapperKind.MUTATION_ROOT_FIELD),
[_utils.MapperKind.SUBSCRIPTION_ROOT_FIELD]: (...args) =>
configHandler(...args, _utils.MapperKind.SUBSCRIPTION_ROOT_FIELD),
[_utils.MapperKind.INTERFACE_FIELD]: (...args) =>
configHandler(...args, _utils.MapperKind.INTERFACE_FIELD),
[_utils.MapperKind.COMPOSITE_FIELD]: (...args) =>
configHandler(...args, _utils.MapperKind.COMPOSITE_FIELD),
[_utils.MapperKind.INPUT_OBJECT_FIELD]: (...args) =>
configHandler(...args, _utils.MapperKind.INPUT_OBJECT_FIELD),
[_utils.MapperKind.ARGUMENT]: (...args) =>
configHandler(...args, _utils.MapperKind.ARGUMENT)
};
return {
directables,
directiveName,
directiveSdl,
optionsTypeName,
optionsSdl,
transformer: (schema) => {
return (0, _utils.mapSchema)(schema, HANDLER_MAP);
}
};
}
const MAPPER_KIND_TO_KIND_MAP = Object.freeze({
[_utils.MapperKind.OBJECT_FIELD]: _microfiber.KINDS.OBJECT,
[_utils.MapperKind.QUERY_ROOT_FIELD]: _microfiber.KINDS.OBJECT,
[_utils.MapperKind.MUTATION_ROOT_FIELD]: _microfiber.KINDS.OBJECT,
[_utils.MapperKind.SUBSCRIPTION_ROOT_FIELD]: _microfiber.KINDS.OBJECT,
[_utils.MapperKind.INTERFACE_FIELD]: _microfiber.KINDS.INTERFACE,
[_utils.MapperKind.ENUM_VALUE]: _microfiber.KINDS.ENUM
});
const MAPPER_KIND_TO_STUFF_MAP = Object.freeze({
[_utils.MapperKind.ARGUMENT]: {
fnName: 'getArg',
typeKind: _microfiber.KINDS.OBJECT
},
[_utils.MapperKind.INPUT_OBJECT_FIELD]: {
fnName: 'getField',
typeKind: _microfiber.KINDS.INPUT_OBJECT
}
});
const addMetadataFromDirectables = ({
directables,
directiveName,
optionsTypeName,
introspectionQueryResponse,
metadatasWritePath
}) => {
const microfiber = new _microfiber.Microfiber(
introspectionQueryResponse,
MICROFIBER_OPTIONS
);
const typeFnMap = {
ObjectTypeDefinition: ({ type }) =>
microfiber.getType({ kind: _microfiber.KINDS.OBJECT, name: type.name }),
ScalarTypeDefinition: ({ type }) =>
microfiber.getType({ kind: _microfiber.KINDS.SCALAR, name: type.name }),
EnumTypeDefinition: ({ type }) =>
microfiber.getType({ kind: _microfiber.KINDS.ENUM, name: type.name }),
InterfaceTypeDefinition: ({ type }) =>
microfiber.getType({ kind: _microfiber.KINDS.INTERFACE, name: type.name }),
UnionTypeDefinition: ({ type }) =>
microfiber.getType({ kind: _microfiber.KINDS.UNION, name: type.name }),
InputObjectTypeDefinition: ({ type }) =>
microfiber.getType({ kind: _microfiber.KINDS.INPUT_OBJECT, name: type.name })
};
const configFnMap = {
FieldDefinition: ({ type, config, typeName, fieldName, mapperKind }) => {
const typeKind = MAPPER_KIND_TO_KIND_MAP[mapperKind];
if (!typeKind) {
console.error(new Error('Unsupported mapperKind'), {
type,
config,
mapperKind,
typeName,
fieldName
});
return;
}
return microfiber.getField({ typeKind, typeName, fieldName });
},
InputValueDefinition: ({
type,
config,
typeName,
fieldName,
mapperKind,
astNode
}) => {
const { fnName, typeKind } = MAPPER_KIND_TO_STUFF_MAP[mapperKind] || {};
if (!typeKind) {
console.error(new Error('Unsupported mapperKind'), {
type,
config,
mapperKind,
typeName,
fieldName
});
return;
}
return microfiber[fnName]({
typeKind,
typeName,
fieldName,
argName: astNode.name.value
});
},
EnumValueDefinition: ({
type,
config,
typeName,
externalValue,
mapperKind
}) => {
const typeKind = MAPPER_KIND_TO_KIND_MAP[mapperKind];
if (!typeKind) {
console.error(new Error('Unsupported mapperKind'), {
type,
config,
mapperKind,
typeName,
externalValue
});
return;
}
return microfiber.getField({
typeKind,
typeName,
fieldName: externalValue
});
}
};
for (const {
directive,
type,
config,
fieldName,
typeName,
externalValue,
mapperKind
} of directables) {
if (!directive || !Object.keys(directive).length) {
continue;
}
const map = type ? typeFnMap : config ? configFnMap : {};
const astNode = (type || config).astNode;
const fn = map[astNode.kind];
if (fn) {
const typeDef = fn({
type,
config,
typeName,
fieldName,
externalValue,
mapperKind,
astNode
});
if (typeDef) {
(0, _set.default)(typeDef, metadatasWritePath, directive);
} else {
console.error(new Error('Unsupported typeDef'), {
type,
config,
typeName,
fieldName,
externalValue,
mapperKind,
astNode
});
}
} else {
console.error(new Error('Unsupported astNode.kind'), {
type,
config,
typeName,
fieldName,
externalValue,
astNode
});
}
}
microfiber.removeDirective({ name: directiveName });
microfiber.removeType({ kind: _microfiber.KINDS.INPUT_OBJECT, name: optionsTypeName });
return microfiber.getResponse();
};exports.addMetadataFromDirectables = addMetadataFromDirectables;