@cortexql/ts2graphql
Version:
A TypeScrpt transpiler to GraphQL for your project
709 lines • 32.7 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
t[p[i]] = s[p[i]];
return t;
};
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var g = generator.apply(thisArg, _arguments || []), i, q = [];
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
function fulfill(value) { resume("next", value); }
function reject(value) { resume("throw", value); }
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
};
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator];
return m ? m.call(o) : typeof __values === "function" ? __values(o) : o[Symbol.iterator]();
};
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = require("path");
const appRootDir = require("app-root-dir");
const fs = require("fs");
const schemaPrinter_1 = require("graphql/utilities/schemaPrinter");
const createTypeDocJson_1 = require("./utils/createTypeDocJson");
const TypeDoc2GraphQL_1 = require("./utils/TypeDoc2GraphQL");
const graphql_tools_1 = require("graphql-tools");
const pathSync_1 = require("./utils/pathSync");
const capitalize_1 = require("./utils/capitalize");
const graphql_1 = require("graphql");
const glob_1 = require("glob");
const exec_1 = require("./utils/exec");
const Validator_1 = require("./utils/Validator");
const iterall_1 = require("iterall");
__export(require("./utils/TypeDoc2GraphQL"));
__export(require("./utils/Validator"));
if (Symbol.asyncIterator === undefined) {
Symbol.asyncIterator = iterall_1.$$asyncIterator;
}
function graphqlParseOutput(value, context, info) {
return __awaiter(this, void 0, void 0, function* () {
if (Array.isArray(value)) {
return yield Promise.all(value.map(item => graphqlParseOutput(item, context, info)));
}
if (value instanceof Promise) {
return value.then(promisedValue => graphqlParseOutput(promisedValue, context, info));
}
if (typeof value === 'object' && value != null) {
let constructor;
if (typeof value.toGraph === 'function') {
constructor = value.constructor;
value = yield value.toGraph(context, info);
}
if (value instanceof Date) {
return value;
}
else if (typeof value.toJSON === 'function') {
value = value.toJSON();
}
else if (typeof value.toJSDate === 'function') {
return value.toJSDate();
}
else if (typeof value.toString === 'function'
&& value.toString !== Object.prototype.toString) {
return value.toString();
}
else if (typeof value.toNumber === 'function') {
return value.toNumber();
}
// TODO: Add Set and Map handlers
if (typeof value === 'object' && value != null) {
const obj = {};
if (typeof obj.__typename === 'undefined') {
if (typeof constructor !== 'undefined' && typeof constructor.graphqlType !== 'undefined') {
obj.__typename = constructor.graphqlType.name;
}
}
for (const key of Object.keys(value)) {
obj[key] = yield graphqlParseOutput(value[key], context, info);
}
return obj;
}
return value;
}
return value;
});
}
exports.graphqlParseOutput = graphqlParseOutput;
function getValidator(rootPath, validatorDir) {
const validatorsPath = path_1.resolve(rootPath, validatorDir);
const glob = new glob_1.GlobSync('*.@(ts|js)', {
ignore: '*.d.ts',
cwd: validatorsPath,
});
const validatorTypeMap = {};
for (const file of glob.found) {
const fileName = path_1.resolve(validatorsPath, file);
const typeName = path_1.basename(fileName).replace(/\.(ts|js)$/, '');
if (typeName === 'index') {
continue;
}
const validatorFile = require(fileName);
if (!('validators' in validatorFile)) {
throw new Error(`The file ${path_1.relative(rootPath, fileName)} must export validators`);
}
const { validators } = validatorFile;
if (!('sanitize' in validators)) {
throw new Error(`The file ${path_1.relative(rootPath, fileName)} must export validators.sanitize`);
}
if (!('validate' in validators)) {
throw new Error(`The file ${path_1.relative(rootPath, fileName)} must export validators.validate`);
}
validatorTypeMap[typeName] = validators;
}
return new Validator_1.Validator(validatorTypeMap);
}
exports.getValidator = getValidator;
function getScalars(rootPath, scalarsDir) {
const scalarsPath = path_1.resolve(rootPath, scalarsDir);
const glob = new glob_1.GlobSync('*.@(ts|js)', {
ignore: '*.d.ts',
cwd: scalarsPath,
});
const scalars = [];
for (const file of glob.found) {
const fileName = path_1.resolve(scalarsPath, file);
const graphqlTypeName = path_1.basename(fileName).replace(/\.(ts|js)$/, '');
if (graphqlTypeName === 'index') {
continue;
}
const nameMatch = graphqlTypeName.match(/^GraphQL(.*)/);
if (nameMatch === null) {
throw new Error(`The file ${path_1.relative(rootPath, fileName)} must start with GraphQL name`);
}
const scalarFile = require(fileName);
if (!(graphqlTypeName in scalarFile)) {
throw new Error(`The file ${path_1.relative(rootPath, fileName)} must export ${nameMatch[1]}`);
}
const { [graphqlTypeName]: type } = scalarFile;
if (!(type instanceof graphql_1.GraphQLScalarType
|| type instanceof graphql_1.GraphQLObjectType
|| type instanceof graphql_1.GraphQLInputObjectType
|| type instanceof graphql_1.GraphQLInterfaceType
|| type instanceof graphql_1.GraphQLUnionType)) {
throw new Error(`The file ${path_1.relative(rootPath, fileName)} must export a named type`);
}
if (type.name !== nameMatch[1]) {
throw new Error(`The file ${path_1.relative(rootPath, fileName)} must export a type named ${nameMatch[1]}`);
}
scalars.push(type);
}
return scalars;
}
exports.getScalars = getScalars;
class TypeScript2GraphQL extends TypeDoc2GraphQL_1.TypeDoc2GraphQL {
constructor(options = {}) {
const { types, parserHandler } = options;
const paths = Object.assign({ root: appRootDir.get(), types: 'types', query: 'query', mutation: 'mutation', subscription: 'subscription', validators: 'validators', scalars: 'scalars' }, options.paths);
let restPaths;
let rootPath;
({ root: rootPath } = paths, restPaths = __rest(paths, ["root"]));
const stats = fs.lstatSync(rootPath);
if (!stats.isDirectory()) {
throw new Error('The root path must be a directory');
}
for (const name of Object.keys(restPaths)) {
if (restPaths[name] !== undefined) {
restPaths[name] = path_1.resolve(rootPath, restPaths[name]);
}
}
super({
types: (types !== undefined ? types : []).concat(getScalars(rootPath, restPaths.scalars)),
parserHandler: parserHandler !== undefined ? parserHandler : getValidator(rootPath, restPaths.validators),
paths: restPaths,
});
this.rootPath = rootPath;
}
generate() {
if (this.typedoc === undefined) {
const typedoc = createTypeDocJson_1.createTypeDocJson(this.rootPath);
this.initialize(typedoc);
}
}
restoreSourceMap(type, map) {
if (type instanceof graphql_1.GraphQLEnumType && map.values !== undefined) {
const typeValues = type.getValues();
for (const index in typeValues) {
const value = typeValues[index];
typeValues[index].value = map.values[value.name];
}
}
else if (type instanceof graphql_1.GraphQLInputObjectType && map.fields !== undefined) {
const fields = type.getFields();
for (const key of Object.keys(fields)) {
if (Object.prototype.hasOwnProperty.call(fields, key)) {
if (!map.fields.hasOwnProperty(key)) {
continue;
}
if (map.fields[key].hasOwnProperty('validate')) {
fields[key].validate = map.fields[key].validate;
}
if (map.fields[key].hasOwnProperty('sanitize')) {
fields[key].sanitize = map.fields[key].sanitize;
}
}
}
}
else if (type instanceof graphql_1.GraphQLObjectType && map.fields !== undefined) {
const fields = type.getFields();
for (const key of Object.keys(fields)) {
if (!Object.prototype.hasOwnProperty.call(fields, key)) {
continue;
}
const { args } = fields[key];
if (args === undefined) {
continue;
}
for (const index in args) {
const argName = args[index].name;
if (!map.hasOwnProperty('fields')
|| !map.fields.hasOwnProperty(key)
|| map.fields[key].validateArgs === undefined) {
continue;
}
const validateArgs = map.fields[key].validateArgs;
if (validateArgs.hasOwnProperty(argName)) {
args[index].validate = validateArgs[argName];
}
}
}
}
}
restoreSchema(inputDir = 'schema') {
const glob = new glob_1.GlobSync('**/*.graphql', {
cwd: inputDir,
});
const defs = [];
const maps = [];
for (const file of glob.found) {
defs.push(fs.readFileSync(path_1.resolve(inputDir, file)).toString());
const mapJsonFile = path_1.resolve(inputDir, file.replace(/\.graphql$/, '.map.json'));
if (fs.existsSync(mapJsonFile)) {
maps.push(JSON.parse(fs.readFileSync(mapJsonFile).toString()));
}
}
const schema = graphql_1.buildSchema(defs.join('\n\n'));
for (const map of maps) {
const type = schema.getType(map.name);
this.restoreSourceMap(type, map);
}
return schema;
}
dumpSchema(outputDir = 'schema') {
const schema = this.getSchema();
const singleFileMatch = outputDir.match(/([^\/]+)\.graphql$/);
let singleFile = '';
if (singleFileMatch !== null) {
// tslint:disable-next-line no-parameter-reassignment
outputDir = path_1.dirname(outputDir);
if (outputDir === singleFileMatch[0]) {
// tslint:disable-next-line no-parameter-reassignment
outputDir = '.';
}
singleFile = singleFileMatch[1];
}
const outputPath = path_1.resolve(this.rootPath, outputDir);
pathSync_1.pathSync(outputPath);
if (singleFileMatch === null && outputPath !== this.rootPath) {
exec_1.exec(`node_modules/.bin/rimraf ${outputPath}`);
try {
fs.mkdirSync(outputPath);
}
catch (err) { }
}
const types = [
schema.getQueryType(),
schema.getMutationType(),
schema.getSubscriptionType(),
...Object.values(schema.getTypeMap())
];
const parts = [];
for (const type of types) {
if (type == null) {
continue;
}
if (type instanceof graphql_1.GraphQLObjectType
|| type instanceof graphql_1.GraphQLInputObjectType
|| type instanceof graphql_1.GraphQLEnumType
|| type instanceof graphql_1.GraphQLScalarType
|| type instanceof graphql_1.GraphQLInterfaceType) {
if (singleFileMatch === null) {
fs.writeFileSync(path_1.resolve(outputPath, `${type.name}.graphql`), this.getSource(type));
const map = this.getSourceMap(type);
if (map !== undefined) {
fs.writeFileSync(path_1.resolve(outputPath, `${type.name}.map.json`), JSON.stringify(map, null, 2));
}
}
else {
parts.push(this.getSource(type));
}
}
else {
const name = type.name === undefined ? type.constructor.name : type.name;
console.log(`skipping ${name} as it cannot be resolved to source`);
}
}
if (singleFileMatch !== null) {
fs.writeFileSync(path_1.resolve(outputPath, `${singleFile}.graphql`), parts.join('\n\n'));
}
}
getSourceMap(type) {
let map;
if (type instanceof graphql_1.GraphQLEnumType) {
graphql_1.GraphQLEnumType.name;
map = {
name: type.name,
type: 'enum',
values: {},
};
for (const value of type.getValues()) {
map.values[value.name] = value.value;
}
}
else if (type instanceof graphql_1.GraphQLInputObjectType) {
const fields = type.getFields();
const createMap = () => {
if (map === undefined) {
map = {
name: type.name,
type: 'input',
fields: {},
};
}
};
const createFieldMap = (key) => {
createMap();
if (!map.fields.hasOwnProperty(key)) {
map.fields[key] = {};
}
};
for (const key of Object.keys(fields)) {
if (!Object.prototype.hasOwnProperty.call(fields, key)) {
continue;
}
if (fields[key].hasOwnProperty('validate')) {
createFieldMap(key);
map.fields[key].validate = fields[key].validate;
}
if (fields[key].hasOwnProperty('sanitize')) {
createFieldMap(key);
map.fields[key].sanitize = fields[key].sanitize;
}
}
}
else if (type instanceof graphql_1.GraphQLObjectType) {
const fields = type.getFields();
const createMap = () => {
if (map === undefined) {
map = {
name: type.name,
type: 'input',
fields: {},
};
}
};
const createFieldMap = (key) => {
createMap();
if (!map.fields.hasOwnProperty(key)) {
map.fields[key] = {};
}
};
const createFieldValidateArgMap = (key) => {
createFieldMap(key);
if (!map.fields[key].hasOwnProperty('validateArgs')) {
map.fields[key].validateArgs = {};
}
};
const createFieldSanitizeArgMap = (key) => {
createFieldMap(key);
if (!map.fields[key].hasOwnProperty('sanitizeArgs')) {
map.fields[key].sanitizeArgs = {};
}
};
for (const key of Object.keys(fields)) {
if (Object.prototype.hasOwnProperty.call(fields, key)) {
const field = fields[key];
for (const arg of field.args) {
if (arg.hasOwnProperty('validate')) {
createFieldValidateArgMap(key);
map.fields[key].validateArgs[arg.name] = arg.validate;
}
if (arg.hasOwnProperty('sanitize')) {
createFieldSanitizeArgMap(key);
map.fields[key].sanitizeArgs[arg.name] = arg.sanitize;
}
}
}
}
}
return map;
}
injectParserHandlerOnObjectType(type) {
const fields = type.getFields();
for (const fieldName of Object.keys(fields)) {
if (!Object.prototype.hasOwnProperty.call(fields, fieldName)) {
continue;
}
const field = fields[fieldName];
const resolveFunction = field.resolve;
const sanitizeRules = [];
const validateRules = [];
for (const arg of field.args) {
const sanitize = arg.sanitize;
if (sanitize !== undefined) {
for (const parser of sanitize) {
sanitizeRules.push(Object.assign({ path: [arg.name], valueType: arg.type }, parser));
}
}
const validate = arg.validate;
if (validate !== undefined) {
for (const parser of validate) {
validateRules.push(Object.assign({ path: [arg.name], valueType: arg.type }, parser));
}
}
const parseInputObjectTypeArg = (type, path) => {
if (type instanceof graphql_1.GraphQLNonNull || 'ofType' in type) {
// tslint:disable-next-line no-parameter-reassignment
type = type.ofType;
}
if (!(type instanceof graphql_1.GraphQLInputObjectType)) {
return;
}
const input = type;
const inputFields = input.getFields();
for (const inputKey of Object.keys(inputFields)) {
if (!Object.prototype.hasOwnProperty.call(inputFields, inputKey)) {
continue;
}
const inputField = inputFields[inputKey];
const sanitize = inputField.sanitize;
if (sanitize !== undefined) {
for (const parser of sanitize) {
sanitizeRules.push(Object.assign({ path: path.concat(inputKey), valueType: inputField.type }, parser));
}
}
const validate = inputField.validate;
if (validate !== undefined) {
for (const parser of validate) {
validateRules.push(Object.assign({ path: path.concat(inputKey), valueType: inputField.type }, parser));
}
}
if (inputField.type instanceof graphql_1.GraphQLInputObjectType) {
parseInputObjectTypeArg(inputField.type, path.concat(inputKey));
}
}
};
parseInputObjectTypeArg(arg.type, [arg.name]);
}
if (sanitizeRules.length === 0 && validateRules.length === 0) {
continue;
}
field.resolve = (source, args, context, info) => __awaiter(this, void 0, void 0, function* () {
yield this.parserHandler.parse({
type,
fieldName,
args,
sanitizeRules,
validateRules,
source,
context,
info,
});
if (resolveFunction !== undefined) {
return yield resolveFunction(source, args, context, info);
}
return null;
});
}
}
injectParserHandler(schema) {
const typeMap = schema.getTypeMap();
for (const name of Object.keys(typeMap)) {
const type = typeMap[name];
if (type instanceof graphql_1.GraphQLObjectType) {
this.injectParserHandlerOnObjectType(type);
}
}
}
addResolveFunctionsToSchema(schema, resolveFunctions, resolverValidationOptions) {
graphql_tools_1.addResolveFunctionsToSchema(schema, resolveFunctions, resolverValidationOptions);
this.injectParserHandler(schema);
}
getSource(type) {
return schemaPrinter_1.printType(type);
}
getMethodResolvers(dir, name) {
const resolvers = {};
const methodPath = path_1.resolve(this.rootPath, dir);
const glob = new glob_1.GlobSync('*.@(ts|js)', {
ignore: ['*.d.ts'],
cwd: methodPath,
});
for (const file of glob.found) {
const fileName = path_1.resolve(methodPath, file);
const methodName = path_1.basename(fileName).replace(/\.(ts|js)$/, '');
if (methodName === 'index') {
continue;
}
if (!resolvers.hasOwnProperty(name)) {
resolvers[name] = {};
}
const exports = require(fileName);
let resolver;
const upperCased = methodName.charAt(0).toUpperCase() + methodName.slice(1);
if (methodName in exports) {
resolver = exports[methodName];
}
else {
if (name === 'Subscription') {
resolver = exports[`subscribe${upperCased}`];
if (typeof resolver === 'undefined') {
resolver = exports.subscribe;
}
}
else {
resolver = exports[`resolve${upperCased}`];
if (typeof resolver === 'undefined') {
resolver = exports.resolve;
}
}
}
if (typeof resolver === 'undefined') {
throw new Error(`Couldn't find an exported variable for ${name}.${methodName}`);
}
const mappedResolver = (obj, args, context, info) => resolver(args, context, info);
if (name === 'Subscription') {
resolvers[name][methodName] = {
resolve: (obj, args, context, info) => {
if (obj === undefined) {
throw new graphql_1.GraphQLError('You must execute subscription under WebSocket');
}
return graphqlParseOutput(obj[methodName], context, info);
},
subscribe: (obj, args, context, info) => {
const iterable = mappedResolver(obj, args, context, info);
if ('subscribe' in iterable) {
return (function () {
return __asyncGenerator(this, arguments, function* () {
function nextResolver() {
const resolver = {};
resolver.promise = new Promise((resolve, reject) => {
resolver.resolve = resolve;
resolver.reject = reject;
});
return resolver;
}
const buffer = [nextResolver()];
const subscription = iterable.subscribe((value) => {
const { resolve } = buffer[buffer.length - 1];
buffer.push(nextResolver());
resolve({ value });
}, (error) => {
resolver.reject(error);
}, (value) => {
const { resolve } = buffer[buffer.length - 1];
buffer.push(nextResolver());
resolve({ value, done: true });
});
try {
while (true) {
const { value, done } = yield __await((buffer[0].promise));
buffer.shift();
if (done === true) {
return value !== undefined ? { [methodName]: value } : value;
}
yield { [methodName]: value };
}
}
finally {
subscription.dispose();
while (buffer.shift()) { }
}
});
})();
}
if (iterall_1.isAsyncIterable(iterable)) {
return (function () {
return __asyncGenerator(this, arguments, function* () {
try {
for (var iterable_1 = __asyncValues(iterable), iterable_1_1; iterable_1_1 = yield __await(iterable_1.next()), !iterable_1_1.done;) {
const result = yield __await(iterable_1_1.value);
yield { [methodName]: result };
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (iterable_1_1 && !iterable_1_1.done && (_a = iterable_1.return)) yield __await(_a.call(iterable_1));
}
finally { if (e_1) throw e_1.error; }
}
var e_1, _a;
});
})();
}
throw new Error(`Could not resolve subscribe method ${name}.${methodName}, did not return AsyncIterator or Observable`);
},
};
}
else {
resolvers[name][methodName] = (obj, args, context, info) => graphqlParseOutput(mappedResolver(obj, args, context, info), context, info);
}
}
return resolvers;
}
getTypeResolvers(schema) {
const resolvers = {};
const methodPath = path_1.resolve(this.rootPath, 'types');
const glob = new glob_1.GlobSync('*.@(ts|js)', {
ignore: '*.d.ts',
cwd: methodPath,
});
for (const file of glob.found) {
const fileName = path_1.resolve(methodPath, file);
const typeName = path_1.basename(fileName).replace(/\.(ts|js)$/, '');
if (typeName === 'index') {
continue;
}
const type = require(fileName)[typeName];
const graphqlType = schema.getType(typeName);
if (graphqlType instanceof graphql_1.GraphQLObjectType) {
Object.defineProperty(type, 'graphqlType', { value: graphqlType });
Object.defineProperty(type, 'originalType', { value: type });
if (typeof type.isTypeOf === 'function') {
graphqlType.isTypeOf = (value, context, info) => {
return type.isTypeOf(value, context, info);
};
}
}
if (typeof type === 'function') {
const resolveObject = {};
const { graphqlType } = type;
if (typeof graphqlType !== 'undefined') {
const fields = graphqlType.getFields();
for (const fieldName of Object.keys(fields)) {
resolveObject[fieldName] =
(obj, args, context, info) => {
let result = null;
const methodName = `resolve${capitalize_1.capitalize(fieldName)}`;
if (typeof obj[methodName] === 'function') {
result = obj[methodName](args, context, info);
}
else if (obj[fieldName] != null) {
result = obj[fieldName];
}
return graphqlParseOutput(result, context, info);
};
}
}
resolvers[graphqlType.name] = resolveObject;
}
}
return resolvers;
}
getResolvers(schema) {
return Object.assign({}, this.getTypeResolvers(schema), this.getMethodResolvers('query', 'Query'), this.getMethodResolvers('mutation', 'Mutation'), this.getMethodResolvers('subscription', 'Subscription'));
}
getSchemaConfig() {
this.generate();
return super.getSchemaConfig();
}
getSchema() {
const schema = super.getSchema();
return schema;
}
getSchemaWithResolvers() {
const schema = this.getSchema();
const resolvers = this.getResolvers(schema);
this.addResolveFunctionsToSchema(schema, resolvers);
return schema;
}
restoreSchemaWithResolvers(inputDir = 'schema') {
const schema = this.restoreSchema(inputDir);
const resolvers = this.getResolvers(schema);
this.addResolveFunctionsToSchema(schema, resolvers);
return schema;
}
}
exports.TypeScript2GraphQL = TypeScript2GraphQL;
//# sourceMappingURL=index.js.map