UNPKG

gatsby-source-sanity

Version:

Gatsby source plugin for building websites using Sanity.io as a backend.

296 lines 10.8 kB
"use strict"; 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