@backland/schema
Version:
TypeScript schema declaration and validation library with static type inference
177 lines (176 loc) • 6.51 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.VariableType = exports.EnumType = void 0;
exports.buildArgs = buildArgs;
exports.configFields = void 0;
exports.objectToQuery = objectToQuery;
exports.stringifyArgs = stringifyArgs;
class EnumType {
constructor(value) {
this.value = value;
}
}
exports.EnumType = EnumType;
class VariableType {
constructor(value) {
this.value = value;
}
toJSON() {
return `$${this.value}`;
}
}
exports.VariableType = VariableType;
const configFields = ['__args', '__alias', '__aliasFor', '__variables', '__directives', '__on', '__all_on', '__typeName', '__name'];
exports.configFields = configFields;
function stringifyArgs(obj_from_json) {
if (obj_from_json instanceof EnumType) {
return obj_from_json.value;
}
// variables should be prefixed with dollar sign and not quoted
else if (obj_from_json instanceof VariableType) {
return `$${obj_from_json.value}`;
}
// Cheers to Derek: https://stackoverflow.com/questions/11233498/json-stringify-without-quotes-on-properties
else if (typeof obj_from_json !== 'object' || obj_from_json === null) {
// not an object, stringify using native function
return JSON.stringify(obj_from_json);
} else if (Array.isArray(obj_from_json)) {
return `[${obj_from_json.map(item => stringifyArgs(item)).join(', ')}]`;
}
// Implements recursive object serialization according to JSON spec
// but without quotes around the keys.
const props = Object.keys(obj_from_json).map(key => `${key}: ${stringifyArgs(obj_from_json[key])}`).join(', ');
return `{${props}}`;
}
function buildArgs(argsObj) {
const args = [];
for (const argName in argsObj) {
args.push(`${argName}: ${stringifyArgs(argsObj[argName])}`);
}
return args.join(', ');
}
function buildVariables(varsObj) {
const args = [];
for (const varName in varsObj) {
args.push(`$${varName}: ${varsObj[varName]}`);
}
return args.join(', ');
}
function buildDirectives(dirsObj) {
const directiveName = Object.keys(dirsObj)[0];
const directiveValue = dirsObj[directiveName];
if (typeof directiveValue === 'boolean' || typeof directiveValue === 'object' && Object.keys(directiveValue).length === 0) {
return directiveName;
} else if (typeof directiveValue === 'object') {
const args = [];
for (const argName in directiveValue) {
const argVal = stringifyArgs(directiveValue[argName]).replace(/"/g, '');
args.push(`${argName}: ${argVal}`);
}
return `${directiveName}(${args.join(', ')})`;
} else {
throw new Error(`Unsupported type for directive: ${typeof directiveValue}. Types allowed: object, boolean.\n` + `Offending object: ${JSON.stringify(dirsObj)}`);
}
}
function getIndent(level) {
return Array(level * 2 + 1).join(' ');
}
function filterNonConfigFields(fieldName, ignoreFields) {
// Returns true if fieldName is not a 'configField'.
return configFields.indexOf(fieldName) == -1 && ignoreFields.indexOf(fieldName) == -1;
}
function convertQuery(node, level, output, options) {
Object.keys(node).filter(key => filterNonConfigFields(key, options.ignoreFields)).forEach(key => {
let value = node[key];
if (typeof value === 'object') {
if (Array.isArray(value)) {
value = value.find(item => item && typeof item === 'object');
if (!value) {
output.push([`${key}`, level]);
return;
}
}
const fieldCount = Object.keys(value).filter(keyCount => filterNonConfigFields(keyCount, options.ignoreFields)).length;
const subFields = fieldCount > 0;
const argsExist = typeof value.__args === 'object' && Object.keys(value.__args).length > 0;
const directivesExist = typeof value.__directives === 'object';
const fullFragmentsExist = value.__all_on instanceof Array;
const partialFragmentsExist = typeof value.__on === 'object';
let token = `${key}`;
if (typeof value.__name === 'string') {
token = `${token} ${value.__name}`;
}
if (typeof value.__aliasFor === 'string') {
token = `${token}: ${value.__aliasFor}`;
}
if (typeof value.__variables === 'object' && Object.keys(value.__variables).length > 0) {
token = `${token} (${buildVariables(value.__variables)})`;
} else if (argsExist || directivesExist) {
let argsStr = '';
let dirsStr = '';
if (directivesExist) {
dirsStr = Object.entries(value.__directives).map(item => `@${buildDirectives({
[item[0]]: item[1]
})}`).join(' ');
}
if (argsExist) {
argsStr = `(${buildArgs(value.__args)})`;
}
const spacer = directivesExist && argsExist ? ' ' : '';
token = `${token} ${argsStr}${spacer}${dirsStr}`;
}
output.push([token + (subFields || partialFragmentsExist || fullFragmentsExist ? ' {' : ''), level]);
convertQuery(value, level + 1, output, options);
if (fullFragmentsExist) {
value.__all_on.forEach(fullFragment => {
output.push([`...${fullFragment}`, level + 1]);
});
}
if (partialFragmentsExist) {
const inlineFragments = value.__on instanceof Array ? value.__on : [value.__on];
inlineFragments.forEach(inlineFragment => {
const name = inlineFragment.__typeName;
output.push([`... on ${name} {`, level + 1]);
convertQuery(inlineFragment, level + 2, output, options);
output.push(['}', level + 1]);
});
}
if (subFields || partialFragmentsExist || fullFragmentsExist) {
output.push(['}', level]);
}
} else if (options.includeFalsyKeys === true || value) {
output.push([`${key}`, level]);
}
});
}
function objectToQuery(query, options = {}) {
if (!query || typeof query != 'object') {
throw new Error('query object not specified');
}
if (Object.keys(query).length == 0) {
throw new Error('query object has no data');
}
if (!(options.ignoreFields instanceof Array)) {
options.ignoreFields = [];
}
const queryLines = [];
convertQuery(query, 0, queryLines, options);
let output = '';
queryLines.forEach(([line, level]) => {
if (options.pretty) {
if (output) {
output += '\n';
}
output += getIndent(level) + line;
} else {
if (output) {
output += ' ';
}
output += line;
}
});
return output;
}
//# sourceMappingURL=objectToQuery.js.map