gatsby-source-sanity
Version:
Gatsby source plugin for building websites using Sanity.io as a backend.
296 lines • 10.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_1 = require("lodash");
const graphql_1 = require("gatsby/graphql");
const crypto = require("crypto");
const mockSchemaValues = require("easygraphql-mock");
const flatted_1 = require("flatted");
const debug_1 = require("../debug");
const deepCopy_1 = require("./deepCopy");
const normalize_1 = require("./normalize");
const conflictPrefix = 'sanity';
const builtins = ['ID', 'String', 'Boolean', 'Int', 'Float', 'JSON', 'DateTime', 'Date'];
const wantedNodeTypes = ['ObjectTypeDefinition', 'UnionTypeDefinition', 'InterfaceTypeDefinition'];
const wantedScalarTypes = ['Date', 'JSON'];
const blockExample = [
{
_key: 'abc123',
_type: 'block',
style: 'normal',
list: 'bullet',
markDefs: [{ _key: 'abc', _type: 'link', href: 'https://www.sanity.io/' }],
children: [{ _key: 'bcd', _type: 'span', text: 'Sanity', marks: ['em', 'abc'] }],
},
];
exports.getExampleValues = (ast, config, typeMap) => {
const transformedAst = transformAst(ast);
const transformed = graphql_1.print(transformedAst);
const objectTypes = (transformedAst.definitions || [])
.filter(def => def.kind === 'ObjectTypeDefinition')
.map(def => def.name.value);
debug_1.default('Schema used for mocking values:\n\n%s', transformed);
let mockedValues = {};
try {
const mocked = mockSchemaValues(transformed, {
Date: '2018-01-01',
JSON: blockExample,
});
// Delete mocked values for non-object types
Object.keys(mocked).forEach(typeName => {
if (!objectTypes.includes(typeName)) {
delete mocked[typeName];
}
});
mockedValues = mocked;
}
catch (err) {
debug_1.default('Failed to mock values from transformed schema: %s', err.stack);
}
const copiedValues = deepCopy_1.deepCopy(mockedValues, 20);
return addGatsbyNodeValues(copiedValues, config, typeMap);
};
function transformAst(ast) {
const root = Object.assign({}, ast, { definitions: ast.definitions
.filter(isWantedAstNode)
.map(transformDefinitionNode)
.concat(getScalarTypeDefs()) });
return root;
}
function isWantedAstNode(astNode) {
const node = astNode;
if (wantedNodeTypes.includes(node.kind) && node.name.value !== 'RootQuery') {
return true;
}
return false;
}
function transformDefinitionNode(node) {
switch (node.kind) {
case 'ObjectTypeDefinition':
return transformObjectTypeDefinition(node);
case 'UnionTypeDefinition':
return transformUnionTypeDefinition(node);
case 'InterfaceTypeDefinition':
return transformInterfaceTypeDefinition(node);
default:
return node;
}
}
function transformObjectTypeDefinition(astNode) {
const node = astNode;
const fields = node.fields || [];
const jsonTargets = fields.map(getJsonAliasTargets).filter(Boolean);
const blockFields = jsonTargets.map(makeBlockField);
return Object.assign({}, node, { name: Object.assign({}, node.name, { value: getTypeName(node.name.value) }), fields: [
...fields.filter(field => !getJsonAliasTargets(field)).map(transformFieldNodeAst),
...blockFields,
] });
}
function transformUnionTypeDefinition(node) {
return Object.assign({}, node, { types: (node.types || []).map(maybeRewriteType), name: Object.assign({}, node.name, { value: getTypeName(node.name.value) }) });
}
function transformInterfaceTypeDefinition(node) {
const fields = node.fields || [];
return Object.assign({}, node, { fields: fields.map(transformFieldNodeAst), name: Object.assign({}, node.name, { value: getTypeName(node.name.value) }) });
}
function unwrapType(typeNode) {
if (['NonNullType', 'ListType'].includes(typeNode.kind)) {
const wrappedType = typeNode;
return unwrapType(wrappedType.type);
}
return typeNode;
}
function getJsonAliasTargets(field) {
const alias = (field.directives || []).find(dir => dir.name.value === 'jsonAlias');
if (!alias) {
return null;
}
const forArg = (alias.arguments || []).find(arg => arg.name.value === 'for');
if (!forArg) {
return null;
}
return graphql_1.valueFromAST(forArg.value, graphql_1.GraphQLString, {});
}
function makeBlockField(name) {
return {
kind: 'FieldDefinition',
name: {
kind: 'Name',
value: name,
},
arguments: [],
directives: [],
type: {
kind: 'NonNullType',
type: {
kind: 'ListType',
type: {
kind: 'NonNullType',
type: {
kind: 'NamedType',
name: {
kind: 'Name',
value: 'SanityBlock',
},
},
},
},
},
};
}
function makeNonNullable(nodeType) {
if (nodeType.kind === 'ListType') {
const unwrapped = maybeRewriteType(unwrapType(nodeType));
return {
kind: 'NonNullType',
type: {
kind: 'ListType',
type: makeNonNullable(unwrapped),
},
};
}
if (nodeType.kind === 'NamedType') {
return { kind: 'NonNullType', type: maybeRewriteType(nodeType) };
}
const targetType = maybeRewriteType(nodeType.type);
return { kind: 'NonNullType', type: targetType };
}
function transformFieldNodeAst(node) {
return Object.assign({}, node, { name: maybeRewriteFieldName(node), type: makeNonNullable(node.type), description: undefined, directives: [] });
}
function maybeRewriteType(nodeType) {
const type = nodeType;
if (typeof type.name === 'undefined') {
return nodeType;
}
// Gatsby has a date type, but not a datetime, so rewire it
if (type.name.value === 'DateTime') {
return Object.assign({}, type, { name: { kind: 'Name', value: 'Date' } });
}
if (builtins.includes(type.name.value)) {
return type;
}
return Object.assign({}, type, { name: { kind: 'Name', value: getTypeName(type.name.value) } });
}
function maybeRewriteFieldName(field) {
if (!normalize_1.RESTRICTED_NODE_FIELDS.includes(field.name.value)) {
return field.name;
}
return Object.assign({}, field.name, { value: `${conflictPrefix}${lodash_1.upperFirst(field.name.value)}` });
}
function getTypeName(name) {
return name.startsWith('Sanity') ? name : `Sanity${name}`;
}
function hash(content) {
return crypto
.createHash('md5')
.update(flatted_1.stringify(content))
.digest('hex');
}
function addGatsbyNodeValues(map, config, typeMap) {
const idPrefix = `mock--${config.projectId}-${config.dataset}`;
const initial = {};
return Object.keys(map).reduce((acc, typeName) => {
if (!lodash_1.isPlainObject(map[typeName])) {
return acc;
}
const existingValue = map[typeName];
const newValue = rewriteReferences(existingValue, { exampleValueMap: map, typeMap, idPrefix });
const node = Object.assign({ id: `${idPrefix}-${typeName}`, parent: null, children: [] }, newValue, { internal: {
type: typeName,
contentDigest: hash(newValue),
} });
return Object.assign({}, acc, { [typeName]: removeTypeName(node) });
}, initial);
}
function rewriteReferences(existingValue, options) {
const { exampleValueMap: map, idPrefix, typeMap } = options;
const typeName = getValueType(existingValue, map);
const type = typeName && typeMap.objects[typeName];
const initial = {};
return Object.keys(existingValue).reduce((acc, key) => {
const field = type && type.fields[key];
const isExplicitRef = field && field.isReference;
const isImplicitRef = !isExplicitRef && isImplicitReference(existingValue[key], map);
if (isExplicitRef) {
for (let typeName in map) {
if (map[typeName].__typename === existingValue[key].__typename && existingValue[key]._id) {
acc[`${key}___NODE`] = `${idPrefix}-${typeName}`;
return acc;
}
}
}
else if (isImplicitRef && Array.isArray(existingValue[key])) {
acc[`${key}___NODE`] = existingValue[key]
.map((item) => {
const memberType = getValueType(item, map);
return memberType ? `${idPrefix}-${memberType}` : false;
})
.filter(Boolean);
return acc;
}
else if (isImplicitRef) {
const memberType = getValueType(existingValue[key], map);
acc[`${key}___NODE`] = `${idPrefix}-${memberType}`;
return acc;
}
else if (lodash_1.isPlainObject(existingValue[key])) {
acc[key] = rewriteReferences(existingValue[key], options);
return acc;
}
else if (Array.isArray(existingValue[key])) {
acc[key] = existingValue[key].map((item) => lodash_1.isPlainObject(item) ? rewriteReferences(item, options) : item);
return acc;
}
acc[key] = existingValue[key];
return acc;
}, initial);
}
function removeTypeName(obj, seen = new Set()) {
if (seen.has(obj)) {
return obj;
}
seen.add(obj);
if (Array.isArray(obj)) {
return obj.map(item => removeTypeName(item, seen));
}
if (obj === null || typeof obj !== 'object') {
return obj;
}
for (const prop in obj) {
if (prop === '__typename') {
delete obj[prop];
}
else {
removeTypeName(obj[prop], seen);
}
}
return obj;
}
function getScalarTypeDefs() {
return wantedScalarTypes.map((name) => {
const scalar = {
kind: 'ScalarTypeDefinition',
name: { kind: 'Name', value: name },
};
return scalar;
});
}
function isImplicitReference(fieldValue, exampleValueMap) {
if (Array.isArray(fieldValue)) {
const first = fieldValue[0];
return isImplicitReference(first, exampleValueMap);
}
if (fieldValue && fieldValue._id) {
return Boolean(getValueType(fieldValue, exampleValueMap));
}
return false;
}
function getValueType(value, exampleValueMap) {
for (let typeName in exampleValueMap) {
if (exampleValueMap[typeName].__typename === value.__typename) {
return typeName;
}
}
return false;
}
//# sourceMappingURL=getExampleValues.js.map