graphql-s2s
Version:
Transpile an enriched GraphQL string schema (e.g. support for metadata, inheritance, generic types, ...) into the standard string schema understood by graphql.js and the Apollo server client.
1,185 lines (1,055 loc) • 1.92 MB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else {
var a = factory();
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
}
})(this, function() {
return /******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ "./src/graphmetadata.js":
/*!******************************!*\
!*** ./src/graphmetadata.js ***!
\******************************/
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
/**
* Copyright (c) 2018, Neap Pty Ltd.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
var _ = __webpack_require__(/*! lodash */ "./node_modules/lodash/lodash.js");
var _require = __webpack_require__(/*! ./utilities */ "./src/utilities.js"),
chain = _require.chain,
log = _require.log,
escapeGraphQlSchema = _require.escapeGraphQlSchema,
removeMultiSpaces = _require.removeMultiSpaces,
matchLeftNonGreedy = _require.matchLeftNonGreedy,
newShortId = _require.newShortId;
var carrReturnEsc = '░';
var tabEsc = '_t_';
/**
* Remove directives
* @param {String} schema Escaped schema (i.e., without tabs or carriage returns. The CR have been replaced by '░' )
* @return {String} output.schema Schema without directives
* @return {Array} output.directives
* @return {String} output.directives[0].name Directive's name
* @return {String} output.directives[0].definition Directive's definition
* @return {Array} output.directives[0].instances
* @return {String} output.directives[0].instances[0].id Unique identifier that replaces the directive's instance value
* @return {String} output.directives[0].instances[0].value Directive's instance value
*/
var removeDirectives = function removeDirectives() {
var schema = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
if (!schema) return {
schema: schema,
directives: null
};
schema += '░';
var directives = [];
var d = schema.match(/directive\s(.*?)@(.*?)(\((.*?)\)\son\s(.*?)░|\son\s(.*?)░)/mg) || [];
d.forEach(function (directive) {
var directiveName = directive.match(/@(.*?)[\s(]/)[0].replace(/(░)\s/g, '').trim().replace('(', '');
schema = schema.replace(directive, '');
if (!schema.match(/░$/)) schema += '░';
var dInstances = schema.match(new RegExp("".concat(directiveName, "(.*?)\u2591"), 'g')) || [];
var instances = [];
dInstances.forEach(function (dInst) {
var id = "_".concat(newShortId(), "_");
var inst = dInst.replace(/░$/, '');
schema = schema.replace(inst, id);
instances.push({
id: id,
value: inst
});
});
directives.push({
name: directiveName.replace('@', ''),
body: directive,
directive: true,
directiveValues: instances
});
}); // Get the rogue directives, i.e., the directives defined immediately after a field (must be on the same line)
// and have not been escaped before because they do not have an explicit definition in the current schema (scenario
// of AWS AppSync where the @aws_subscribe is defined outside of the developer reach)
//
// IMPORTANT: The code below mutates the 'schema' variable
var rogueDirectives = (schema.replace(/░/g, '░░').match(/░\s*[a-zA-Z0-9_]+([^░]*?)@(.*?)░/g) || []).map(function (m) {
return m.replace(/^(.*?)@/, '@').replace(/\s*░$/, '');
}).reduce(function (acc, m) {
m = m.trim().replace(/{$/, '');
var directiveName = m.match(/^@[a-zA-Z0-9_]+/)[0].slice(1);
var directiveInstanceId = "_".concat(newShortId(), "_");
schema = schema.replace(m, directiveInstanceId);
if (acc[directiveName]) acc[directiveName].directiveValues.push({
id: directiveInstanceId,
value: m
});else {
acc.push(directiveName);
acc[directiveName] = {
name: directiveName,
body: '',
directive: true,
directiveValues: [{
id: directiveInstanceId,
value: m
}]
};
}
return acc;
}, []);
if (rogueDirectives.length > 0) directives.push.apply(directives, _toConsumableArray(rogueDirectives.map(function (x) {
return rogueDirectives[x];
})));
return {
schema: schema,
directives: directives
};
};
var reinsertDirectives = function reinsertDirectives() {
var schema = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var directives = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
if (!schema) return schema;
var directiveDefinitions = directives.map(function (x) {
return x.body;
}).join('░');
directives.forEach(function (_ref) {
var _ref$directiveValues = _ref.directiveValues,
directiveValues = _ref$directiveValues === void 0 ? [] : _ref$directiveValues;
return directiveValues.forEach(function (_ref2) {
var id = _ref2.id,
value = _ref2.value;
schema = schema.replace(id, value);
});
});
return "".concat(directiveDefinitions).concat(schema);
};
/**
* Extracts the graph metadata as well as the directives from a GraphQL schema
*
* @param {string} schema GraphQL schema containing Graph metadata (e.g. @node, @edge, ...)
* @return {Array} graphMetadata
* @return {String} graphMetadata.escSchema Escaped schema
* @return {String} graphMetadata[0].name
* @return {String} graphMetadata[0].body
* @return {String} graphMetadata[0].schemaType
* @return {String} graphMetadata[0].schemaName
* @return {String} graphMetadata[0].parent
* @return {String} graphMetadata[0].directive
* @return {String} graphMetadata[0].directiveValues
*/
var extractGraphMetadata = function extractGraphMetadata() {
var schema = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var _removeDirectives = removeDirectives(escapeGraphQlSchema(schema, carrReturnEsc, tabEsc).replace(/_t_/g, ' ')),
escSchema = _removeDirectives.schema,
directives = _removeDirectives.directives;
var attrMatches = escSchema.match(/@(.*?)(░)(.*?)({|░)/mg);
var graphQlMetadata = chain(_(attrMatches).map(function (m) {
return chain(m.split(carrReturnEsc)).next(function (parts) {
if (parts.length < 2) throw new Error("Schema error: Misused metadata attribute in '".concat(parts.join(' '), ".'"));
var typeMatch = "".concat(parts[0].trim(), " ").match(/@(.*?)(\s|{|\(|\[)/);
if (!typeMatch) {
var msg = "Schema error: Impossible to extract type from metadata attribute ".concat(parts[0]);
log(msg);
throw new Error(msg);
}
var attrName = typeMatch[1].trim();
var attrBody = parts[0].replace("@".concat(attrName), '').trim();
var _chain$next$val = chain(removeMultiSpaces(parts[1].trim())).next(function (t) {
return t.match(/^(type\s|input\s|enum\s|interface\s)/) ? chain(t.split(' ')).next(function (bits) {
return {
schemaType: bits[0].toUpperCase(),
value: bits[1].replace(/ /g, '').replace(/{$/, '')
};
}).val() : {
schemaType: 'PROPERTY',
value: t
};
}).val(),
schemaType = _chain$next$val.schemaType,
value = _chain$next$val.value;
var parent = schemaType == 'PROPERTY' ? chain(escSchema.split(m).join('___replace___')).next(function (s) {
return matchLeftNonGreedy(s, '(type |input |enum |interface )', '___replace___');
}).next(function (m2) {
if (!m2) throw new Error("Schema error: Property '".concat(value, "' with metadata '@").concat(value, "' does not live within any schema type (e.g. type, enum, interface, input, ...)"));
var parentSchemaType = m2[1].trim().toUpperCase();
var parentSchemaTypeName = m2[2].replace(/{/g, ' ').replace(/░/g, ' ').trim().split(' ')[0];
return {
type: parentSchemaType,
name: parentSchemaTypeName
};
}).val() : null;
return {
name: attrName,
body: attrBody,
schemaType: schemaType,
schemaName: value,
parent: parent
};
}).val();
})).next(function (metadata) {
return metadata.map(function (m) {
return m.schemaType == 'PROPERTY' ? m.parent ? chain(metadata.find(function (x) {
return x.schemaType == m.parent.type && x.schemaName == m.parent.name;
})).next(function (v) {
return v ? function () {
m.parent.metadata = {
type: v.schemaType,
name: v.name
};
return m;
}() : m;
}).val() : m : m;
});
}).next(function (metadata) {
return _.toArray(metadata).concat(directives);
}).val() || [];
graphQlMetadata.escSchema = escSchema;
return graphQlMetadata;
};
/**
* [description]
* @param {String} schema Schema with non-standard syntax.
* @return {String} output.stdSchema Schema without all the non-standard metadata.
* @return {[Metatdata]} output.metadata Array of Metadata object
*/
var removeGraphMetadata = function removeGraphMetadata() {
var schema = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var meta = extractGraphMetadata(schema) || [];
var directives = meta.filter(function (m) {
return m.directive;
});
var schemaWithNoMeta = (reinsertDirectives(meta.escSchema.replace(/@(.*?)░/g, ''), directives) || '').replace(/░/g, '\n');
return {
stdSchema: schemaWithNoMeta,
metadata: meta
};
};
module.exports = {
extractGraphMetadata: extractGraphMetadata,
removeGraphMetadata: removeGraphMetadata
};
/***/ }),
/***/ "./src/graphqls2s.js":
/*!***************************!*\
!*** ./src/graphqls2s.js ***!
\***************************/
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
/**
* Copyright (c) 2018, Neap Pty Ltd.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
// Inheritance:
// ============
// _getObjWithExtensions: This function is the one that compiles types that inherits from others.
//
// Generic Types:
// ==============
// _createNewSchemaObjectFromGeneric: This function is the one that creates new types from Generic Types.
var _ = __webpack_require__(/*! lodash */ "./node_modules/lodash/lodash.js");
var _require = __webpack_require__(/*! ./utilities */ "./src/utilities.js"),
chain = _require.chain,
log = _require.log,
escapeGraphQlSchema = _require.escapeGraphQlSchema,
getQueryAST = _require.getQueryAST,
buildQuery = _require.buildQuery,
newShortId = _require.newShortId,
isScalarType = _require.isScalarType;
var _require2 = __webpack_require__(/*! ./graphmetadata */ "./src/graphmetadata.js"),
extractGraphMetadata = _require2.extractGraphMetadata,
removeGraphMetadata = _require2.removeGraphMetadata;
var GENERICTYPEREGEX = /<(.*?)>/;
var TYPENAMEREGEX = /type\s(.*?){/;
var INPUTNAMEREGEX = /input\s(.*?){/;
var ENUMNAMEREGEX = /enum\s(.*?){/;
var INTERFACENAMEREGEX = /interface\s(.*?){/;
var ABSTRACTNAMEREGEX = /abstract\s(.*?){/;
var INHERITSREGEX = /inherits\s+[\w<>]+(?:\s*,\s*\w+)*/g;
var IMPLEMENTSREGEX = /implements\s(.*?)\{/mg;
var PROPERTYPARAMSREGEX = /\((.*?)\)/;
var TYPE_REGEX = {
regex: /(extend type|type)\s(.*?){(.*?)░([^#]*?)}/mg,
type: 'type'
};
var INPUT_REGEX = {
regex: /(extend input|input)\s(.*?){(.*?)░([^#]*?)}/mg,
type: 'input'
};
var ENUM_REGEX = {
regex: /enum\s(.*?){(.*?)░([^#]*?)}/mg,
type: 'enum'
};
var INTERFACE_REGEX = {
regex: /(extend interface|interface)\s(.*?){(.*?)░([^#]*?)}/mg,
type: 'interface'
};
var ABSTRACT_REGEX = {
regex: /(extend abstract|abstract)\s(.*?){(.*?)░([^#]*?)}/mg,
type: 'abstract'
};
var SCALAR_REGEX = {
regex: /(.{1}|.{0})scalar\s(.*?)([^\s]*?)(?![a-zA-Z0-9])/mg,
type: 'scalar'
};
var UNION_REGEX = {
regex: /(.{1}|.{0})union([^\n]*?)\n/gm,
type: 'union'
};
var carrReturnEsc = '░';
var tabEsc = '_t_';
var _s = {};
var escapeGraphQlSchemaPlus = function escapeGraphQlSchemaPlus(sch, cr, t) {
if (!sch) return sch;
if (!_s[sch]) _s[sch] = escapeGraphQlSchema(sch, cr, t);
return _s[sch];
};
var escapeDirectives = function escapeDirectives(str, metadata) {
var directives = (metadata || []).filter(function (_ref) {
var directiveValues = _ref.directiveValues;
return directiveValues && directiveValues[0] && directiveValues[0].id && directiveValues[0].value;
}).reduce(function (acc, _ref2) {
var directiveValues = _ref2.directiveValues;
acc.push.apply(acc, _toConsumableArray(directiveValues));
return acc;
}, []);
return directives.reduce(function (acc, _ref3) {
var id = _ref3.id,
value = _ref3.value;
acc[0] = acc[0].replace(value, id);
acc[1].push({
id: id,
value: value
});
return acc;
}, [str, []]);
};
/**
* Gets a first rough breakdown of the string schema.
*
* @param {String} sch Original GraphQl Schema
* @param {String} metadata[].name e.g., "cypher"
* @param {String} metadata[].body
* @param {Boolean} metadata[].directive
* @param {String} metadata[].directiveValues[].id e.g., "_RghS1T9k5_"
* @param {String} metadata[].directiveValues[].value e.g., "@cypher(statement: \"CREATE (a:Area {name: $name, creationDate: timestamp()}) RETURN a\")"
* @return {Array} Using regex, the interfaces, types, inputs, enums and abstracts entities are isolated
* e.g. [{
* property: 'type Query { bars: [Bar]! }',
* block: [ 'bars: [Bar]!' ],
* extend: false
* },{
* property: 'type Bar { id: ID }',
* block: [ 'id: ID' ],
* extend: false
* }]
*/
var _getSchemaBits = function _getSchemaBits() {
var sch = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var metadata = arguments.length > 1 ? arguments[1] : undefined;
var escapedSchemaWithComments = escapeGraphQlSchemaPlus(sch, carrReturnEsc, tabEsc);
var comments = [].concat(_toConsumableArray(escapedSchemaWithComments.match(/#(.*?)░/g) || []), _toConsumableArray(escapedSchemaWithComments.match(/"""░(.*?)░\s*"""░/g) || []), _toConsumableArray(escapedSchemaWithComments.match(/"([^"]+)"░/g) || []));
var _comments$reduce = comments.reduce(function (acc, m) {
var commentToken = "#".concat(newShortId(), "\u2591");
acc.schema = acc.schema.replace(m, commentToken);
acc.tokens.push({
id: commentToken,
value: m
});
return acc;
}, {
schema: escapedSchemaWithComments,
tokens: []
}),
escSchemaWithEscComments = _comments$reduce.schema,
tokens = _comments$reduce.tokens; // We append '\n' to help isolating the 'union'
var schemaWithoutComments = ' ' + sch.replace(/#(.*?)\n/g, '').replace(/"""\s*\n([^]*?)\n\s*"""\s*\n/g, '').replace(/"([^"]+)"\n/g, '') + '\n';
var escapedSchemaWithoutComments = escapeGraphQlSchemaPlus(schemaWithoutComments, carrReturnEsc, tabEsc);
var _escapeDirectives = escapeDirectives(escSchemaWithEscComments, metadata),
_escapeDirectives2 = _slicedToArray(_escapeDirectives, 2),
escapedSchemaWithEscCommentsAndDirectives = _escapeDirectives2[0],
directives = _escapeDirectives2[1];
return _.flatten([TYPE_REGEX, INPUT_REGEX, ENUM_REGEX, INTERFACE_REGEX, ABSTRACT_REGEX, SCALAR_REGEX, UNION_REGEX].map(function (rx) {
// 1. Apply the regex matching
return chain((rx.type == 'scalar' ? escapedSchemaWithoutComments : rx.type == 'union' ? schemaWithoutComments : escapedSchemaWithEscCommentsAndDirectives).match(rx.regex) || []) // 2. Filter the right matches
.next(function (regexMatches) {
return rx.type == 'scalar' ? regexMatches.filter(function (m) {
return m.indexOf('scalar') == 0 || m.match(/^(?![a-zA-Z0-9])/);
}) : rx.type == 'union' ? regexMatches.filter(function (m) {
return m.indexOf('union') == 0 || m.match(/^(?![a-zA-Z0-9])/);
}) : regexMatches;
}) // 3. Replace the escaped comments with their true value
.next(function (regexMatches) {
return regexMatches.map(function (b) {
return (b.match(/#(.*?)░/g) || []).reduce(function (acc, m) {
var value = (tokens.find(function (t) {
return t.id == m;
}) || {}).value;
return value ? acc.replace(m, value) : acc;
}, b);
});
}) // 4. Breackdown each match into 'property', 'block' and 'extend'
.next(function (regexMatches) {
var transform = rx.type == 'scalar' ? _breakdownScalarBit : rx.type == 'union' ? _breakdownUnionBit : function (str) {
return _breakdownSchemabBit(str, directives);
};
return regexMatches.map(function (str) {
return transform(str);
});
}).val();
}));
};
var _breakdownSchemabBit = function _breakdownSchemabBit(str, directives) {
directives = directives || [];
var blockMatch = str.match(/{(.*?)░([^#]*?)}/);
if (!blockMatch) {
var msg = 'Schema error: Missing block';
log(msg);
throw new Error(msg);
}
var _directives$reduce = directives.reduce(function (acc, _ref4) {
var id = _ref4.id,
value = _ref4.value;
acc[0] = acc[0].replace(id, value);
acc[1] = acc[1].replace(id, value);
return acc;
}, [blockMatch[0], str.split(carrReturnEsc).join(' ').split(tabEsc).join(' ').replace(/ +(?= )/g, '').trim()]),
_directives$reduce2 = _slicedToArray(_directives$reduce, 2),
blockWithDirectives = _directives$reduce2[0],
rawProperty = _directives$reduce2[1];
var block = _.toArray(_(blockWithDirectives.replace(/_t_/g, '').replace(/^{/, '').replace(/}$/, '').split(carrReturnEsc).map(function (x) {
return x.trim();
})).filter(function (x) {
return x != '';
}));
var _ref5 = rawProperty.indexOf('extend') == 0 ? {
property: rawProperty.replace('extend ', ''),
extend: true
} : {
property: rawProperty,
extend: false
},
property = _ref5.property,
extend = _ref5.extend;
return {
property: property,
block: block,
extend: extend
};
};
var _breakdownScalarBit = function _breakdownScalarBit(str) {
var block = (str.split(' ').slice(-1) || [])[0];
return {
property: "scalar ".concat(block),
block: block,
extend: false
};
};
var _breakdownUnionBit = function _breakdownUnionBit(str) {
var block = str.replace(/(^union\s|\sunion\s|\n)/g, '').trim();
return {
property: "union ".concat(block),
block: block,
extend: false
};
};
/**
*
* @param {String} firstLine First line of a code block (e.g., 'type Page {')
* @return {String} output.type Valid values: 'TYPE', 'ENUM', 'INPUT', 'INTERFACE', 'UNION', 'SCALAR'
* @return {String} output.name e.g., 'Page'
*/
var _getSchemaEntity = function _getSchemaEntity(firstLine) {
return firstLine.indexOf('type') == 0 ? {
type: 'TYPE',
name: firstLine.match(/type\s+(.*?)\s+.*/)[1].trim()
} : firstLine.indexOf('enum') == 0 ? {
type: 'ENUM',
name: firstLine.match(/enum\s+(.*?)\s+.*/)[1].trim()
} : firstLine.indexOf('input') == 0 ? {
type: 'INPUT',
name: firstLine.match(/input\s+(.*?)\s+.*/)[1].trim()
} : firstLine.indexOf('interface') == 0 ? {
type: 'INTERFACE',
name: firstLine.match(/interface\s+(.*?)\s+.*/)[1].trim()
} : firstLine.indexOf('union') == 0 ? {
type: 'UNION',
name: firstLine.match(/union\s+(.*?)\s+.*/)[1].trim()
} : firstLine.indexOf('scalar') == 0 ? {
type: 'SCALAR',
name: firstLine.match(/scalar\s+(.*?)\s+.*/)[1].trim()
} : {
type: null,
name: null
};
};
/**
* Gets all the comments associated to the schema blocks.
*
* @param {String} sch Raw GraphQL schema.
* @return {String} output[].text Comment
* @return {String} output[].property.type Valid values: 'TYPE', 'ENUM', 'INPUT', 'INTERFACE', 'UNION', 'SCALAR'
* @return {String} output[].property.name Property name (e.g., 'User' if the block started with 'type User {').
*/
var _getCommentsBits = function _getCommentsBits(sch) {
return (escapeGraphQlSchemaPlus(sch, carrReturnEsc, tabEsc).match(/░\s*[#"](.*?)░([^#"]*?)({|}|:)/g) || []).filter(function (x) {
return x.match(/{$/);
}).map(function (c) {
var parts = _(c.split(carrReturnEsc).map(function (l) {
return l.replace(/_t_/g, ' ').trim();
})).filter(function (x) {
return x != '';
});
var hashCount = parts.reduce(function (a, b) {
a.count = a.count + (b.indexOf('#') == 0 || b.indexOf('"') == 0 || a.inComment ? 1 : 0);
if (b.indexOf('"""') === 0) {
a.inComment = !a.inComment;
}
return a;
}, {
count: 0,
inComment: false
}).count;
return {
text: parts.initial(),
property: _getSchemaEntity(parts.last()),
comments: hashCount == parts.size() - 1
};
}).filter(function (x) {
return x.comments;
}).map(function (x) {
return {
text: x.text.join('\n'),
property: x.property
};
});
};
/**
* Gets the alias for a generic type (e.g. Paged<Product> -> PagedProduct)
* @param {String} genName e.g. Paged<Product>
* @return {String} e.g. PagedProduct
*/
var _genericDefaultNameAlias = function _genericDefaultNameAlias(genName) {
if (!genName) return '';
var m = genName.match(GENERICTYPEREGEX);
if (m) {
var parts = genName.split(m[0]);
return "".concat(parts[0]).concat(m[1].split(',').map(function (x) {
return x.trim();
}).join(''));
} else return genName;
};
/**
* Example: [T] -> [User], or T -> User or Toy<T> -> Toy<User>
* @param {string} genericType e.g. 'Toy<T>', 'Toy<T,U>'
* @param {array} genericLetters e.g. ['T'], ['T','U']
* @param {string} concreteType e.g. 'User', 'User,Product'
* @return {string} e.g. 'Toy<User>', 'Toy<User,Product>'
*/
var _replaceGenericWithType = function _replaceGenericWithType(genericType, genericLetters, concreteType) {
return chain({
gType: genericType.replace(/\s/g, ''),
gLetters: genericLetters.map(function (x) {
return x.replace(/\s/g, '');
}),
cTypes: concreteType.split(',').map(function (x) {
return x.replace(/\s/g, '');
})
}).next(function (_ref6) {
var gType = _ref6.gType,
gLetters = _ref6.gLetters,
cTypes = _ref6.cTypes;
var cTypesLength = cTypes.length;
var genericTypeIsArray = gType.indexOf('[') == 0 && gType.indexOf(']') > 0;
var endingChar = gType.match(/!$/) ? '!' : '';
if (gLetters.length != cTypesLength) throw new Error("Invalid argument exception. Mismatch between the number of types in 'genericLetters' (".concat(genericLetters.join(','), ") and 'concreteType' (").concat(concreteType, ").")); // e.g. genericType = 'T', genericLetters = ['T'], concreteType = 'User' -> resp = 'User'
if (gLetters.length == 1 && gType.replace(/!$/, '') == gLetters[0]) return "".concat(cTypes[0]).concat(endingChar); // e.g. genericType = 'Paged<T>' or '[Paged<T>]'
else if (gType.indexOf('<') > 0 && gType.indexOf('>') > 0) {
var type = genericTypeIsArray ? gType.match(/\[(.*?)\]/)[1] : gType;
var typeName = type.match(/.*</)[0].replace(/<$/, '').trim(); // e.g. 'Toy'
var types = type.match(/<(.*?)>/)[1].split(',').map(function (x) {
return x.trim();
});
if (types.length != gLetters.length) throw new Error("Invalid argument exception. Mismatch between the number of types in 'genericLetters' (".concat(genericLetters.join(','), ") and 'genericType' (").concat(genericType, ")."));
var matchingConcreteTypes = types.map(function (t) {
for (var i = 0; i < cTypesLength; i++) {
if (gLetters[i] == t) return cTypes[i];
}
throw new Error("Invalid argument exception. Mismatch types between the 'genericType' (".concat(genericType, ") and the allowed types 'genericLetters' (").concat(genericLetters.join(','), ")."));
});
var result = "".concat(typeName, "<").concat(matchingConcreteTypes.join(','), ">");
return genericTypeIsArray ? "[".concat(result, "]").concat(endingChar) : "".concat(result).concat(endingChar);
} else {
// e.g. genericType = 'T' or '[T]'
var _type = genericTypeIsArray ? gType.match(/\[(.*?)\]/)[1] : gType;
var _matchingConcreteTypes = _type.split(',').map(function (t) {
var isRequired = /!$/.test(t);
t = (isRequired ? t.replace(/!$/, '') : t).trim();
for (var i = 0; i < cTypesLength; i++) {
if (gLetters[i] == t) return "".concat(cTypes[i]).concat(isRequired ? '!' : '');
}
throw new Error("Invalid argument exception. Mismatch types between the 'genericType' (".concat(genericType, ") and the allowed types 'genericLetters' (").concat(genericLetters.join(','), ")."));
});
var _result = _matchingConcreteTypes.join(',');
return genericTypeIsArray ? "[".concat(_result, "]").concat(endingChar) : "".concat(_result).concat(endingChar);
}
}).val();
};
var memoizedGenericNameAliases = {};
var _getAliasName = function _getAliasName(genericType, metadata) {
if (memoizedGenericNameAliases[genericType]) return memoizedGenericNameAliases[genericType];
var genericStart = genericType.match(/.*</)[0];
var aliasObj = Array.isArray(metadata) ? _getAllAliases(metadata).find(function (x) {
return x.schemaName.indexOf(genericStart) == 0;
}) : metadata && metadata.name == 'alias' ? metadata : null;
var alias = aliasObj && aliasObj.body ? getGenericAlias(aliasObj.body)(genericType) : _genericDefaultNameAlias(genericType);
memoizedGenericNameAliases[genericType] = alias;
return alias;
};
var memoizedAliases = null;
var _getAllAliases = function _getAllAliases(metadata) {
return memoizedAliases || chain((metadata || []).filter(function (x) {
return x.name == 'alias';
})).next(function (aliases) {
memoizedAliases = aliases;
return aliases;
}).val();
};
var memoizedGenericSchemaObjects = {};
/**
* Get all the type details
*
* @param {String} t Type (e.g. Paged<Product> or Paged<T,U>)
* @param {Array} metadata Array of metadata objects
* @param {Array} genericParentTypes Array of string representing the types (e.g. ['T', 'U']) of the generic parent type
* of that type if that type was extracted from a block. If this array is null, that
* means the parent type was not a generic type.
* @return {String} result.originName 't'
* @return {Boolean} result.isGen Indicates if 't' is a generic type
* @return {Boolean} result.dependsOnParent Not null if 't' is a generic. Indicates if the generic type of 't' depends
* on its parent's type (if true, then that means the parent is itself a generic)
* @return {Array} result.metadata 'metadata'
* @return {Array} result.genericParentTypes If the parent is a generic type, then ths array contains contain all the
* underlying types.
* @return {String} result.name If 't' is not a generic type then 't' otherwise determine what's new name.
*/
var _getTypeDetails = function _getTypeDetails(t, metadata, genericParentTypes) {
return chain((t.match(GENERICTYPEREGEX) || [])[1]).next(function (genTypes) {
var isGen = genTypes ? true : false;
var genericTypes = isGen ? genTypes.split(',').map(function (x) {
return x.trim();
}) : null;
var originName = t.replace(/@.+/, '').trim();
var directive = (t.match(/@.+/) || [])[0];
var endingChar = originName.match(/!$/) ? '!' : '';
var dependsOnParent = isGen && genericParentTypes && genericParentTypes.length > 0 && genericTypes.some(function (x) {
return genericParentTypes.some(function (y) {
return x == y;
});
});
return {
originName: originName,
directive: directive,
isGen: isGen,
dependsOnParent: dependsOnParent,
metadata: metadata,
genericParentTypes: genericParentTypes,
name: isGen && !dependsOnParent ? "".concat(_getAliasName(originName, metadata)).concat(endingChar) : originName
};
}).next(function (result) {
if (result.isGen && !memoizedGenericSchemaObjects[result.name]) memoizedGenericSchemaObjects[result.name] = result;
return result;
}).val();
};
/**
* Transpile parameters if generic types are used in them
*
* @param {String} params Parameters (e.g. (filter: Filtered<Product>)
* @param {Array} metadata Array of metadata objects
* @param {Array} genericParentTypes Array of string representing the types (e.g. ['T', 'U']) of the generic parent type
* of that type if that type was extracted from a block. If this array is null, that
* means the parent type was not a generic type.
* @return {String} transpiledParams The transpiled parameters
*/
var _getTranspiledParams = function _getTranspiledParams(params, genericParentTypes) {
return chain(params.split(',')).next(function (genTypes) {
var transpiledParams = [];
genTypes.forEach(function (genType) {
var genericTypeMatches = genType.match(GENERICTYPEREGEX);
var isGen = !!genericTypeMatches;
var genericTypes = isGen ? genTypes.map(function (x) {
return x.trim();
}) : null;
if (!genType) return;
var _genType$split$map = genType.split(':').map(function (item) {
return item.trim();
}),
_genType$split$map2 = _slicedToArray(_genType$split$map, 2),
paramName = _genType$split$map2[0],
originName = _genType$split$map2[1];
var endingChar = originName.match(/!$/) ? '!' : '';
var dependsOnParent = isGen && genericParentTypes && genericParentTypes.length > 0 && genericTypes.some(function (x) {
return genericParentTypes.some(function (y) {
return x === y;
});
});
var result = {
paramName: paramName,
originName: originName,
isGen: isGen,
name: isGen && !dependsOnParent ? "".concat(_getAliasName(originName)).concat(endingChar) : originName
};
if (result.isGen && !memoizedGenericSchemaObjects[result.name]) memoizedGenericSchemaObjects[result.name] = result;
transpiledParams.push("".concat(result.paramName, ": ").concat(result.name));
});
return transpiledParams;
}).next(function (result) {
return result.join(', ');
}).val();
};
var _getPropertyValue = function _getPropertyValue(_ref7, mapResultName) {
var name = _ref7.name,
params = _ref7.params,
result = _ref7.result;
var leftPart = "".concat(name).concat(params ? "(".concat(params, ")") : '');
var delimiter = '';
var rightPart = '';
if (result && result.name) {
delimiter = ': ';
rightPart = mapResultName ? mapResultName(result.name) : result.name;
if (result.directive) rightPart = "".concat(rightPart, " ").concat(result.directive);
}
return "".concat(leftPart).concat(delimiter).concat(rightPart);
};
/**
* Breaks down a string representing a block { ... } into its various parts.
* @param {string} blockParts String representing your entire block (e.g. { users: User[], posts: Paged<Post> })
* @param {object} baseObj
* @param {string} baseObj.type Type of the object with blockParts (e.g. TYPE, ENUM, ...)
* @param {string} baseObj.name Name of the object with blockParts
* @param {array} baseObj.genericTypes Array of types if the 'baseObj' is a generic type.
* @param {array} metadata Array of object. Each object represents a metadata. Example: { name: 'node', body: '(name:hello)', schemaType: 'PROPERTY', schemaName: 'rating: PostRating!', parent: { type: 'TYPE', name: 'PostUserRating', metadata: [Object] } }
* @return [{
* comments: string,
* details: {
* name: string,
* metadata: {
* name: string,
* body: string,
* schemaType: string,
* schemaName: string,
* parent: {
* type: string,
* name: string,
* metadata: [Object]
* }
* },
* params: string,
* result: {
* originName: string,
* isGen: boolean,
* name: string
* }
* },
* value: string
* }] Property breakdown
*/
var _getBlockProperties = function _getBlockProperties(blockParts, baseObj, metadata) {
return chain(_(metadata).filter(function (m) {
return m.schemaType == 'PROPERTY' && m.parent && m.parent.type == baseObj.type && m.parent.name == baseObj.name;
})).next(function (meta) {
return _(blockParts).reduce(function (a, part) {
var p = part.trim();
var mData = meta.filter(function (m) {
return m.schemaName == p;
}).first() || null;
if (p.indexOf('#') == 0 || p.indexOf('"') == 0 || a.insideComment) {
if (p.indexOf('"""') === 0) {
a.insideComment = !a.insideComment;
}
a.comments.push(p);
} else {
var prop = p.replace(/ +(?= )/g, '').replace(/,$/, '');
var paramsMatch = prop.replace(/@.+/, '').match(PROPERTYPARAMSREGEX);
var propDetails = paramsMatch ? chain(prop.split(paramsMatch[0])).next(function (parts) {
return {
name: parts[0].trim(),
metadata: mData,
params: _getTranspiledParams(paramsMatch[1], baseObj.genericTypes),
result: _getTypeDetails((parts[1] || '').replace(':', '').trim(), metadata, baseObj.genericTypes)
};
}).val() : chain(prop.split(':')).next(function (parts) {
return {
name: parts[0].trim(),
metadata: mData,
params: null,
result: _getTypeDetails(parts.slice(1).join(':').trim(), metadata, baseObj.genericTypes)
};
}).val();
a.props.push({
comments: a.comments.join('\n '),
details: propDetails,
value: _getPropertyValue(propDetails)
});
a.comments = [];
}
return a;
}, {
insideComment: false,
comments: [],
props: []
}).props;
}).val();
};
/**
* [description]
* @param {Array} definitions Array of objects ({ property:..., block: [...], extend: ... }) coming from the '_getSchemaBits' function
* @param {String} typeName e.g. 'type' or 'input'
* @param {RegExp} nameRegEx Regex that can extract the specific details of the schema bit (i.e. definitions)
* @param {Array} metadata metadata coming from the 'extractGraphMetadata' method.
* @return {Array} Array of objects: Example:
* [{
* type: 'TYPE',
* extend: false,
* name: 'Foo',
* metadata: null,
* genericType: null,
* blockProps: [ { comments: '', details: [Object], value: 'id: String!' } ],
* inherits: null,
* implements: null },
* {
* type: 'TYPE',
* extend: true,
* name: 'Query',
* metadata: null,
* genericType: null,
* blockProps: [ { comments: '', details: [Object], value: 'foos: [Foo]!' } ],
* inherits: null,
* implements: null
* }]
*/
var _getSchemaObject = function _getSchemaObject(definitions, typeName, nameRegEx, metadata) {
return _.toArray(_(definitions).filter(function (d) {
return d.property.indexOf(typeName) == 0;
}).map(function (d) {
if (typeName == 'scalar') return {
type: 'SCALAR',
extend: false,
name: d.block,
metadata: null,
genericType: false,
blockProps: [],
inherits: null,
"implements": null
};else if (typeName == 'union') return {
type: 'UNION',
extend: false,
name: d.block,
metadata: null,
genericType: false,
blockProps: [],
inherits: null,
"implements": null
};else {
var typeDefMatch = d.property.match(/(.*?){/);
if (!typeDefMatch || typeDefMatch[0].indexOf('#') >= 0) throw new Error("Schema error: Syntax error in '".concat(d.property, "'. Cannot any find schema type definition."));
var typeDef = typeDefMatch[0];
var nameMatch = typeDef.match(nameRegEx);
if (!nameMatch) throw new Error("Schema error: ".concat(typeName, " with missing name."));
var name = nameMatch[1].trim().split(' ')[0];
var genericTypeMatch = name.match(GENERICTYPEREGEX);
var isGenericType = genericTypeMatch ? genericTypeMatch[1] : null;
var inheritsMatch = typeDef.match(INHERITSREGEX);
var superClass = inheritsMatch && inheritsMatch[0].replace('inherits', '').trim().split(',').map(function (v) {
return v.trim();
}) || null;
var implementsMatch = typeDef.match(IMPLEMENTSREGEX);
var directive = (typeDef.match(/@[a-zA-Z0-9_]+(.*?)$/) || [''])[0].trim().replace(/{$/, '').trim() || null;
var _interface = implementsMatch ? implementsMatch[0].replace('implements ', '').replace('{', '').split(',').map(function (x) {
return x.trim().split(' ')[0];
}) : null;
var objectType = typeName.toUpperCase();
var metadat = metadata ? _(metadata).filter(function (m) {
return m.schemaType == objectType && m.schemaName == name;
}).first() || null : null;
var genericTypes = isGenericType ? isGenericType.split(',').map(function (x) {
return x.trim();
}) : null;
var baseObj = {
type: objectType,
name: name,
genericTypes: genericTypes
};
var result = {
type: objectType,
extend: d.extend,
name: name,
metadata: metadat,
directive: directive,
genericType: isGenericType,
blockProps: _getBlockProperties(d.block, baseObj, metadata),
inherits: superClass,
"implements": _interface
};
return result;
}
}));
};
var getGenericAlias = function getGenericAlias(s) {
return !s ? _genericDefaultNameAlias : function (genName) {
return chain(genName.match(GENERICTYPEREGEX)).next(function (m) {
return m ? chain(m[1].split(',').map(function (x) {
return "\"".concat(x.trim(), "\"");
}).join(',')).next(function (genericTypeName) {
return eval(s + '(' + genericTypeName + ')');
}).val() : genName;
}).val();
};
};
var _getInterfaces = function _getInterfaces(definitions, metadata) {
return _getSchemaObject(definitions, 'interface', INTERFACENAMEREGEX, metadata);
};
var _getAbstracts = function _getAbstracts(definitions, metadata) {
return _getSchemaObject(definitions, 'abstract', ABSTRACTNAMEREGEX, metadata);
};
var _getTypes = function _getTypes(definitions, metadata) {
return _getSchemaObject(definitions, 'type', TYPENAMEREGEX, metadata);
};
var _getInputs = function _getInputs(definitions, metadata) {
return _getSchemaObject(definitions, 'input', INPUTNAMEREGEX, metadata);
};
var _getEnums = function _getEnums(definitions, metadata) {
return _getSchemaObject(definitions, 'enum', ENUMNAMEREGEX, metadata);
};
var _getScalars = function _getScalars(definitions, metadata) {
return _getSchemaObject(definitions, 'scalar', null, metadata);
};
var _getUnions = function _getUnions(definitions, metadata) {
return _getSchemaObject(definitions, 'union', null, metadata);
};
/**
* [description]
* @param {String} genericTypeName e.g., 'Page<User,Product>'
* @return {String} e.g., 'Page<0,1>'
*/
var _getCanonicalGenericType = function _getCanonicalGenericType(genericTypeName) {
var _ref8 = (genericTypeName || '').match(/<(.*?)>/) || [],
_ref9 = _slicedToArray(_ref8, 2),
types = _ref9[1];
if (!types) return '';
var canon = types.split(',').map(function (_, idx) {
return idx;
}).join(',');
return genericTypeName.replace(/<(.*?)>/, "<".concat(canon, ">"));
};
/**
* Determines if a generic type is defined in the Schema.
*
* @param {String} schemaTypeName e.g., Page<User>
* @param {[SchemaType]} rawSchemaTypes All the available Schema Types. For example, if there is a { name: 'Page<T>' }, then
* this function returns true
* @return {Boolean} output
*/
var _isGenericTypeDefined = function _isGenericTypeDefined(schemaTypeName, rawSchemaTypes) {
if (!schemaTypeName || !rawSchemaTypes || rawSchemaTypes.length === 0) return false;
var canonicalSchemaTypeName = _getCanonicalGenericType(schemaTypeName);
if (!canonicalSchemaTypeName) return false;
var canonicalGenericTypeNames = rawSchemaTypes.filter(function (_ref10) {
var genericType = _ref10.genericType;
return genericType;
}).map(function (_ref11) {
var name = _ref11.name;
return _getCanonicalGenericType(name);
});
return canonicalGenericTypeNames.some(function (name) {
return name === canonicalSchemaTypeName;
});
};
var _getDefaultGenericName = function _getDefaultGenericName(concreteGenericTypeName) {
return (concreteGenericTypeName || '').replace(/[<>,\s]/g, '');
};
var _memoizedConcreteGenericTypes = {};
/**
* [description]
* @param {String} concreteGenericTypeName Generic type name (e.g., 'Paged<User>')
* @param {[SchemaType]} rawSchemaTypes Array of not fully compiled Schema type objects.
* @param {[Comments]} comments comments[].text, comments[].property.type, comments[].property.name
* @param {String} aliasName Overides the default name. For example. If 'concreteGenericTypeName' is 'Paged<User>'
* its default name is 'PageUser'.
* @return {SchemaType} Resolved Schema Type object.
*/
var _resolveGenericType = function _resolveGenericType(_ref12) {
var concreteGenericTypeName = _ref12.concreteGenericTypeName,
rawSchemaTypes = _ref12.rawSchemaTypes,
comments = _ref12.comments,
aliasName = _ref12.aliasName;
// 1. Returns if the result was already memoized before.
var defaultConcreteName = aliasName || _getDefaultGenericName(concreteGenericTypeName);
if (_memoizedConcreteGenericTypes[defaultConcreteName]) return _memoizedConcreteGenericTypes[defaultConcreteName]; // 2. Find the Generic definition type in the 'rawSchemaTypes'
var genericTypePrefix = ((concreteGenericTypeName.match(/.+</) || [])[0] || '').replace(/\[/g, ''); // e.g., Paged<
if (!genericTypePrefix) throw new Error("Schema error: Cannot find type in generic object ".concat(concreteGenericTypeName));
var genericDefType = rawSchemaTypes.find(function (_ref13) {
var name = _ref13.name;
return name.indexOf(genericTypePrefix) == 0;
});
if (!genericDefType) throw new Error("Schema error: Cannot find any definition for generic type starting with ".concat(genericTypePrefix));else if (!genericDefType.genericType) throw new Error("Schema error: Schema object ".concat(genericDefType.name, " is not generic!")); // 3. Resolve the types and the inherited types
// 3.1. Resolve the types (e.g., if concreteGenericTypeName is 'Paged<User,Product>', typeNames is ['User', 'Product'])
var typeNames = ((concreteGenericTypeName.match(/<(.*?)>/) || [])[1] || '').split(',').map(function (x) {
return x.trim();
}).filter(function (x) {
return x;
}); // 3.1.1. WARNING: This code creates side-effects by mutating '_memoizedConcreteGenericTypes'.
// This is the intended goal as '_memoizedConcreteGenericTypes' is used to in '_getSchemaBits' to get the new generic ASTs.
typeNames.map(function (typeName) {
if (isScalarType(typeName)) return;
_getType(typeName, rawSchemaTypes, comments);
}); // 3.2. Resolve the inherited types
var superClasses = (genericDefType.inherits || []).map(function (superClassName) {
return _getType(superClassName, rawSchemaTypes, comments);
}); // 3.2.1. WARNING: This code creates side-effects by mutating '_memoizedConcreteGenericTypes'.
// This is the intended goal as '_memoizedConcreteGenericTypes' is used to in '_getSchemaBits' to get the new generic ASTs.
superClasses.map(function (superClass) {
if (!_inheritingIsAllowed(genericDefType, superClass)) {
throw new Error('Schema error: ' + genericDefType.type.toLowerCase() + ' ' + genericDefType.name + ' cannot inherit from ' + superClass.type + ' ' + superClass.name + '.');
}
return _resolveSchemaType(superClass, rawSchemaTypes, comments);
}); // 4. Resolving each property of the generic type definition based on the concrete type.
var blockProps = genericDefType.blockProps.map(function (prop) {
var p = prop;
var concreteType = typeNames.join(',');
if (isTypeGeneric(prop.details.result.name, genericDefType.genericType)) {
var details = {
name: prop.details.name,
params: prop.params,
result: {
originName: prop.details.originName,
isGen: prop.details.isGen,
name: _replaceGenericWithType(prop.details.result.name, genericDefType.genericType.split(','), concreteType)
}
}; // 4.1. This is a case where this property is from a generic type similar to type Paged<T> { data:User<T> }. The property
// 'data' depends on parent.
if (prop.details.result.dependsOnParent) {
var propTypeIsRequired = prop.details.result.name.match(/!$/);
var propTypeName = propTypeIsRequired ? prop.details.result.name.replace(/!$/, '') : prop.details.result.name;