gatsby-source-strapi
Version:
Gatsby source plugin for building websites using Strapi as a data source
326 lines (313 loc) • 10.5 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
exports.__esModule = true;
exports.createNodes = void 0;
var _get2 = _interopRequireDefault(require("lodash/get"));
var _toUpper2 = _interopRequireDefault(require("lodash/toUpper"));
var _isPlainObject2 = _interopRequireDefault(require("lodash/isPlainObject"));
var _helpers = require("./helpers");
/*eslint no-undef: "error"*/
/**
* Create a child node for json fields
* @param {Object} json value
* @param {Object} ctx
* @returns {Object} gatsby node
*/
const prepareJSONNode = (json, context) => {
const {
createContentDigest,
createNodeId,
parentNode,
attributeName
} = context;
const jsonNodeId = createNodeId(`${parentNode.strapi_document_id_or_regular_id}-${parentNode.internal.type}-${attributeName}-JSONNode`);
const JSONNode = {
...((0, _isPlainObject2.default)(json) ? {
...json
} : {
strapi_json_value: json
}),
id: jsonNodeId,
parent: parentNode.id,
children: [],
internal: {
type: (0, _toUpper2.default)(`${parentNode.internal.type}_${attributeName}_JSONNode`),
mediaType: `application/json`,
content: JSON.stringify(json),
contentDigest: createContentDigest(json)
}
};
return JSONNode;
};
/**
* Create a child node for relation and link the parent node to it
* @param {Object} relation
* @param {Object} ctx
* @returns {Object} gatsby node
*/
const prepareRelationNode = (relation, context) => {
const {
schemas,
createNodeId,
createContentDigest,
parentNode,
targetSchemaUid
} = context;
// const targetSchema = getContentTypeSchema(schemas, targetSchemaUid);
// const {
// schema: { singularName },
// } = targetSchema;
const nodeType = (0, _helpers.makeParentNodeName)(schemas, targetSchemaUid);
const strapi_document_id_or_regular_id = relation.documentId || relation.id; // support both v5 and v4
const relationNodeId = createNodeId(`${nodeType}-${strapi_document_id_or_regular_id}`);
const node = {
...relation,
id: relationNodeId,
documentId: relation.documentId,
strapi_id: relation.id,
strapi_document_id_or_regular_id,
parent: parentNode.id,
children: [],
internal: {
type: nodeType,
content: JSON.stringify(relation),
contentDigest: createContentDigest(relation)
}
};
return node;
};
/**
* Create a child node for markdown fields
* @param {String} text value
* @param {Object} ctx
* @returns {Object} gatsby node
*/
const prepareTextNode = (text, context) => {
const {
createContentDigest,
createNodeId,
parentNode,
attributeName
} = context;
const textNodeId = createNodeId(`${parentNode.strapi_document_id_or_regular_id}-${parentNode.internal.type}-${attributeName}-TextNode`);
const textNode = {
id: textNodeId,
parent: parentNode.id,
children: [],
[attributeName]: text,
internal: {
type: (0, _toUpper2.default)(`${parentNode.internal.type}_${attributeName}_TextNode`),
mediaType: `text/markdown`,
content: text,
contentDigest: createContentDigest(text)
}
};
return textNode;
};
/**
* Create a child node for media and link the parent node to it
* @param {Object} media
* @param {Object} ctx
* @returns {Object} gatsby node
*/
const prepareMediaNode = (media, context) => {
const {
createNodeId,
createContentDigest,
parentNode
} = context;
const nodeType = "STRAPI__MEDIA";
const relationNodeId = createNodeId(`${nodeType}-${media.id}`);
const node = {
...media,
id: relationNodeId,
strapi_id: media.id,
strapi_document_id_or_regular_id: media.id,
parent: parentNode.id,
children: [],
internal: {
type: nodeType,
content: JSON.stringify(media),
contentDigest: createContentDigest(media)
}
};
return node;
};
/**
* Returns an array of the main node and children nodes to create
* @param {Object} entity the main entry
* @param {String} nodeType the name of the main node
* @param {Object} ctx object of gatsby functions
* @param {String} uid the main schema uid
* @returns {Object[]} array of nodes to create
*/
const createNodes = (entity, context, uid) => {
const nodes = [];
const {
schemas,
createNodeId,
createContentDigest,
getNode
} = context;
const nodeType = (0, _helpers.makeParentNodeName)(schemas, uid);
// f.e. components do not have a documentId, allow regular id
// also, support both v5 documentId and v4 id
const strapi_document_id_or_regular_id = entity.documentId || entity.id;
let entryNode = {
id: createNodeId(`${nodeType}-${strapi_document_id_or_regular_id}`),
documentId: entity.documentId,
strapi_id: entity.id,
strapi_document_id_or_regular_id,
parent: undefined,
children: [],
internal: {
type: nodeType,
content: JSON.stringify(entity),
contentDigest: createContentDigest(entity)
}
};
const schema = (0, _helpers.getContentTypeSchema)(schemas, uid);
for (const attributeName of Object.keys(entity)) {
const value = entity[attributeName];
const attribute = schema.schema.attributes[attributeName];
const type = (0, _get2.default)(attribute, "type");
if (value) {
// Add support for dynamic zones
if (type === "dynamiczone") {
for (const v of value) {
const componentNodeName = (0, _helpers.makeParentNodeName)(schemas, v.strapi_component);
const valueNodes = createNodes(v, context, v.strapi_component).flat();
const compoNodeIds = valueNodes.filter(({
internal
}) => internal.type === componentNodeName).map(({
id
}) => id);
entity[`${attributeName}___NODE`] = [...(entity[`${attributeName}___NODE`] || []), ...compoNodeIds];
for (const n of valueNodes) {
nodes.push(n);
}
}
delete entity[attributeName];
}
if (type === "relation") {
// Create type for the first level of relations, otherwise the user should fetch the other content type
// to link them
const config = {
schemas,
createContentDigest,
createNodeId,
parentNode: entryNode,
attributeName,
targetSchemaUid: attribute.target
};
if (Array.isArray(value)) {
const relationNodes = value.map(relation => prepareRelationNode(relation, config));
entity[`${attributeName}___NODE`] = relationNodes.map(({
id
}) => id);
for (const node of relationNodes) {
if (!getNode(node.id)) {
nodes.push(node);
}
}
} else {
const relationNode = prepareRelationNode(value, config);
entity[`${attributeName}___NODE`] = relationNode.id;
const relationNodeToCreate = getNode(relationNode.id);
if (!relationNodeToCreate) {
nodes.push(relationNode);
}
}
delete entity[attributeName];
}
// Apply transformations to components: markdown, json...
if (type === "component") {
const componentSchema = (0, _helpers.getContentTypeSchema)(schemas, attribute.component);
const componentNodeName = (0, _helpers.makeParentNodeName)(schemas, componentSchema.uid);
if (attribute.repeatable) {
const compoNodes = value.flatMap(v => createNodes(v, context, attribute.component));
// Just link the component nodes and not the components' children one
entity[`${attributeName}___NODE`] = compoNodes.filter(({
internal
}) => internal.type === componentNodeName).map(({
id
}) => id);
for (const node of compoNodes) {
nodes.push(node);
}
} else {
const compoNodes = createNodes(value, context, attribute.component).flat();
// Just link the component node and not the component's children one
entity[`${attributeName}___NODE`] = compoNodes.find(({
internal
}) => internal.type === componentNodeName).id;
for (const node of compoNodes) {
nodes.push(node);
}
}
delete entity[attributeName];
}
// Create nodes for richtext in order to make the markdown-remark plugin works
if (type === "richtext") {
const textNode = prepareTextNode(value.data, {
createContentDigest,
createNodeId,
parentNode: entryNode,
attributeName
});
entity[attributeName][`data___NODE`] = textNode.id;
delete entity[attributeName].data;
nodes.push(textNode);
}
// Create nodes for JSON fields in order to be able to query each field in GraphiQL
// We can remove this if not pertinent
if (type === "json") {
const JSONNode = prepareJSONNode(value, {
createContentDigest,
createNodeId,
parentNode: entryNode,
attributeName
});
entryNode.children = [...entryNode.children, JSONNode.id];
entity[`${attributeName}___NODE`] = JSONNode.id;
// Resolve only the attributeName___NODE and not to both ones
delete entity[attributeName];
nodes.push(JSONNode);
}
if (type == "media") {
const config = {
createContentDigest,
createNodeId,
parentNode: entryNode
};
if (Array.isArray(value)) {
const mediaNodes = value.map(relation => prepareMediaNode(relation, config));
entity[`${attributeName}___NODE`] = mediaNodes.map(({
id
}) => id);
for (const node of mediaNodes) {
if (!getNode(node.id)) {
nodes.push(node);
}
}
} else {
const mediaNode = prepareMediaNode(value, config);
entity[`${attributeName}___NODE`] = mediaNode.id;
const relationNodeToCreate = getNode(mediaNode.id);
if (!relationNodeToCreate) {
nodes.push(mediaNode);
}
}
// Resolve only the attributeName___NODE and not to both ones
delete entity[attributeName];
}
}
}
entryNode = {
...entity,
...entryNode
};
nodes.push(entryNode);
return nodes;
};
exports.createNodes = createNodes;