Last active
November 25, 2021 17:47
-
-
Save voodooattack/e12f1e451f9b82bc20cec1a49e44e23a to your computer and use it in GitHub Desktop.
Applies static directives to GraphQL schema AST.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
}); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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