UNPKG

gatsby-source-sanity

Version:

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

170 lines 6.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const lodash_1 = require("lodash"); const mutator_1 = require("@sanity/mutator"); const graphql_1 = require("gatsby/graphql"); const scalarTypeNames = graphql_1.specifiedScalarTypes.map(def => def.name).concat(['JSON', 'Date']); // Movie => SanityMovie const typePrefix = 'Sanity'; // Node fields used internally by Gatsby. exports.RESTRICTED_NODE_FIELDS = ['id', 'children', 'parent', 'fields', 'internal']; // Transform a Sanity document into a Gatsby node function processDocument(doc, options) { const { createNode, createNodeId, createParentChildLink, createContentDigest, overlayDrafts, skipCreate } = options; const hoistedNodes = []; const rawAliases = getRawAliases(doc, options); const safe = prefixConflictingKeys(doc); const hoisted = hoistMixedArrays(safe, options, { hoistedNodes, path: [doc._id] }); const withRefs = makeNodeReferences(hoisted, options); const node = Object.assign({}, withRefs, rawAliases, { id: createNodeId(overlayDrafts ? unprefixDraftId(doc._id) : doc._id), parent: null, children: [], internal: { mediaType: 'application/json', type: getTypeName(doc._type), contentDigest: createContentDigest(JSON.stringify(withRefs)) } }); if (!skipCreate) { createNode(node); hoistedNodes.forEach(childNode => { const child = childNode; createNode(child); createParentChildLink({ parent: node, child }); }); } return node; } exports.processDocument = processDocument; // `drafts.foo-bar` => `foo.bar` function unprefixDraftId(id) { return id.replace(/^drafts\./, ''); } // movie => SanityMovie // blog_post => SanityBlogPost // sanity.imageAsset => SanityImageAsset function getTypeName(type) { if (!type) { return type; } const typeName = lodash_1.startCase(type); if (scalarTypeNames.includes(typeName)) { return typeName; } return `${typePrefix}${typeName.replace(/\s+/g, '').replace(/^Sanity/, '')}`; } exports.getTypeName = getTypeName; // {foo: 'bar', children: []} => {foo: 'bar', sanityChildren: []} function prefixConflictingKeys(obj) { // Will be overwritten, but initialize for type safety const initial = { _id: '', _type: '' }; return Object.keys(obj).reduce((target, key) => { if (exports.RESTRICTED_NODE_FIELDS.includes(key)) { target[`${lodash_1.camelCase(typePrefix)}${lodash_1.upperFirst(key)}`] = obj[key]; } else { target[key] = obj[key]; } return target; }, initial); } // Transform arrays with both inline objects and references into just references, // adding gatsby child nodes as needed // {body: [{_ref: 'grrm'}, {_type: 'book', name: 'Game of Thrones'}]} // => // {body: [{_ref: 'grrm'}, {_ref: 'someNode'}]} function hoistMixedArrays(obj, options, context) { const { createNodeId, createContentDigest, overlayDrafts } = options; const { hoistedNodes, path } = context; if (lodash_1.isPlainObject(obj)) { const initial = {}; return Object.keys(obj).reduce((acc, key) => { // Skip raw aliases if (key.startsWith('_raw')) { return acc; } acc[key] = hoistMixedArrays(obj[key], options, Object.assign({}, context, { path: path.concat(key) })); return acc; }, initial); } if (Array.isArray(obj)) { let hasRefs = false; let hasNonRefs = false; // First, let's make sure we handle deeply nested structures const hoisted = obj.map((item, index) => { hasRefs = hasRefs || (item && item._ref); hasNonRefs = hasNonRefs || (item && !item._ref); return hoistMixedArrays(item, options, Object.assign({}, context, { path: path.concat(`${index}`) })); }); const hasMixed = hasRefs && hasNonRefs; if (!hasMixed) { return hoisted; } // Now let's hoist non-ref nodes return hoisted.map((item, index) => { if (!item || item._ref) { return item; } const safe = prefixConflictingKeys(item); const withRefs = makeNodeReferences(safe, options); const rawId = path.concat(`${index}`).join('>'); const id = createNodeId(rawId); const parent = createNodeId(overlayDrafts ? unprefixDraftId(path[0]) : path[0]); const childNode = Object.assign({}, withRefs, { id, parent, children: [], internal: { mediaType: 'application/json', type: getTypeName(item._type), contentDigest: createContentDigest(JSON.stringify(withRefs)) } }); hoistedNodes.push(childNode); return { _ref: rawId }; }); } return obj; } function getRawAliases(doc, options) { const { typeMap } = options; const typeName = getTypeName(doc._type); const type = typeMap.objects[typeName]; if (!type) { return doc; } const initial = {}; return Object.keys(type.fields).reduce((acc, fieldName) => { const field = type.fields[fieldName]; if (typeMap.scalars.includes(field.namedType.name.value)) { return acc; } const aliasName = '_' + lodash_1.camelCase(`raw ${fieldName}`); acc[aliasName] = doc[fieldName]; return acc; }, initial); } // Tranform Sanity refs ({_ref: 'foo'}) to Gatsby refs (field___NODE: 'foo') // {author: {_ref: 'grrm'}} => {author___NODE: 'someNodeIdFor-grrm'} function makeNodeReferences(doc, options) { const { createNodeId } = options; const refs = mutator_1.extractWithPath('..[_ref]', doc); if (refs.length === 0) { return doc; } const newDoc = lodash_1.cloneDeep(doc); refs.forEach(match => { const path = match.path.slice(0, -1); const key = path[path.length - 1]; const isArrayIndex = typeof key === 'number'; const referencedId = createNodeId(match.value); if (isArrayIndex) { const arrayPath = path.slice(0, -1); const field = path[path.length - 2]; const nodePath = path.slice(0, -2).concat(`${field}___NODE`); const refPath = nodePath.concat(key); lodash_1.set(newDoc, nodePath, lodash_1.get(newDoc, nodePath, lodash_1.get(newDoc, arrayPath))); lodash_1.set(newDoc, refPath, referencedId); lodash_1.unset(newDoc, arrayPath); } else { const refPath = path.slice(0, -1).concat(`${key}___NODE`); lodash_1.unset(newDoc, path); lodash_1.set(newDoc, refPath, referencedId); } }); return newDoc; } //# sourceMappingURL=normalize.js.map