Skip to content

Instantly share code, notes, and snippets.

@voodooattack
Last active November 25, 2021 17:47
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save voodooattack/e12f1e451f9b82bc20cec1a49e44e23a to your computer and use it in GitHub Desktop.
Save voodooattack/e12f1e451f9b82bc20cec1a49e44e23a to your computer and use it in GitHub Desktop.
Applies static directives to GraphQL schema AST.
import { visit } from 'graphql/language';
/**
* Calls directives with a `resolveStatic` hook at the time of parsing.
* @param ast GraphQL schema AST.
* @param directives The directives collection.
* @param throwOnMissing Should we throw if an unknown directive is encountered?
* @returns {*} Revised AST as transformed by the directives.
*/
export function applyStaticDirectivesToAST(ast, directives, throwOnMissing = true) {
let context = Object.create(null);
return visit(ast, {
enter(node) {
if (node.directives && node.directives.length) {
let current = node;
node.directives.forEach(directive => {
if (!current) return;
const directiveName = directive.name.value;
if (directiveName in directives) {
const staticFunctions = directives[directiveName].resolveStatic;
if (staticFunctions.enter) {
const ret = staticFunctions.enter.call(context, current, directive);
if (typeof ret !== typeof undefined)
current = ret;
}
} else if (throwOnMissing)
throw new Error(`Unknown directive '${directiveName}'`);
});
return current;
}
},
leave(node) {
if (node.directives && node.directives.length) {
let current = node;
node.directives.forEach(directive => {
if (!current) return;
const directiveName = directive.name.value;
if (directiveName in directives) {
const staticFunctions = directives[directiveName].resolveStatic;
if (staticFunctions.leave) {
const ret = staticFunctions.leave.call(context, current, directive);
if (typeof ret !== typeof undefined)
current = ret;
}
}
});
return current;
}
}
});
}
import { applyStaticDirectivesToAST } from './directives';
import { Model } from './model';
import { parse } from 'graphql/language';
const models = {};
const ast = applyStaticDirectivesToAST(parse(`
type User @model
{
id: ID! @primary @storage(type: "UUID", defaultValue: "UUIDV4")
name: String! @index
hash: String! @hidden
salt: String! @hidden
email: String! @index @unique @lowercase
}
`), {
model: {
name: 'model',
description: 'Marks a type as a model.',
resolveStatic:
{
enter(node) {
this.activeModel = models[node.name.value] = new Model(node);
},
leave() {
this.activeModel = null;
}
}
},
primary: {
name: 'primary',
description: 'Marks a field as the primary key for a model.',
resolveStatic: {
enter(node) {
if (!this.activeModel)
throw new Error('Model attributes are not allowed on regular types.');
this.activeModel.setPrimary(node);
}
}
},
index: {
name: 'index',
description: 'Creates a database index for a field.',
resolveStatic: {
enter(node) {
if (!this.activeModel)
throw new Error('Model attributes are not allowed on regular types.');
this.activeModel.setIndex(node);
}
}
},
lowercase: {
name: 'lowercase',
description: 'Marks a field to always be lower case.',
resolveStatic: {
enter(node) {
if (!this.activeModel)
throw new Error('Model attributes are not allowed on regular types.');
this.activeModel.setLowercase(node);
}
}
},
unique: {
name: 'unique',
description: 'Mark a field unique. An additional `index` argument can be specified to create composite indices.',
resolveStatic: {
enter(node, directive) {
if (!this.activeModel)
throw new Error('Model attributes are not allowed on regular types.');
this.activeModel.setUnique(node, directive);
}
}
},
hidden: {
name: 'hidden',
description: `Marks a field as hidden. Hidden fields do not appear in the GraphQL schema,
but have a database representation.`,
resolveStatic: {
leave(node) {
if (!this.activeModel)
throw new Error('Model attributes are not allowed on regular types.');
this.activeModel.setHidden(node);
return null; // delete the AST node
}
}
},
storage: {
name: 'storage',
description: `Storage settings for a field:
type: The database type to use.
defaultValue: The default value to initialize this field with.
`,
resolveStatic: {
enter(node, directive) {
if (!this.activeModel)
throw new Error('Model attributes are not allowed on regular types.');
this.activeModel.setStorage(node, directive);
}
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment