@strapi/utils
Version:
Shared utilities for the Strapi packages
187 lines (184 loc) • 6.89 kB
JavaScript
import { curry, isObject, isNil, clone, isArray } from 'lodash/fp';
import { isRelationalAttribute, isMediaAttribute } from './content-types.mjs';
const traverseEntity = async (visitor, options, entity)=>{
const { path = {
raw: null,
attribute: null,
rawWithIndices: null
}, schema, getModel } = options;
let parent = options.parent;
const traverseMorphRelationTarget = async (visitor, path, entry)=>{
const targetSchema = getModel(entry.__type);
const traverseOptions = {
schema: targetSchema,
path,
getModel,
parent
};
return traverseEntity(visitor, traverseOptions, entry);
};
const traverseRelationTarget = (schema)=>async (visitor, path, entry)=>{
const traverseOptions = {
schema,
path,
getModel,
parent
};
return traverseEntity(visitor, traverseOptions, entry);
};
const traverseMediaTarget = async (visitor, path, entry)=>{
const targetSchemaUID = 'plugin::upload.file';
const targetSchema = getModel(targetSchemaUID);
const traverseOptions = {
schema: targetSchema,
path,
getModel,
parent
};
return traverseEntity(visitor, traverseOptions, entry);
};
const traverseComponent = async (visitor, path, schema, entry)=>{
const traverseOptions = {
schema,
path,
getModel,
parent
};
return traverseEntity(visitor, traverseOptions, entry);
};
const visitDynamicZoneEntry = async (visitor, path, entry)=>{
const targetSchema = getModel(entry.__component);
const traverseOptions = {
schema: targetSchema,
path,
getModel,
parent
};
return traverseEntity(visitor, traverseOptions, entry);
};
// End recursion
if (!isObject(entity) || isNil(schema)) {
return entity;
}
// Don't mutate the original entity object
// only clone at 1st level as the next level will get clone when traversed
const copy = clone(entity);
const visitorUtils = createVisitorUtils({
data: copy
});
const keys = Object.keys(copy);
for(let i = 0; i < keys.length; i += 1){
const key = keys[i];
// Retrieve the attribute definition associated to the key from the schema
const attribute = schema.attributes[key];
const newPath = {
...path
};
newPath.raw = isNil(path.raw) ? key : `${path.raw}.${key}`;
newPath.rawWithIndices = isNil(path.rawWithIndices) ? key : `${path.rawWithIndices}.${key}`;
if (!isNil(attribute)) {
newPath.attribute = isNil(path.attribute) ? key : `${path.attribute}.${key}`;
}
// Visit the current attribute
const visitorOptions = {
data: copy,
schema,
key,
value: copy[key],
attribute,
path: newPath,
getModel,
parent
};
await visitor(visitorOptions, visitorUtils);
// Extract the value for the current key (after calling the visitor)
const value = copy[key];
// Ignore Nil values or attributes
if (isNil(value) || isNil(attribute)) {
continue;
}
// The current attribute becomes the parent once visited
parent = {
schema,
key,
attribute,
path: newPath
};
if (isRelationalAttribute(attribute)) {
const isMorphRelation = attribute.relation.toLowerCase().startsWith('morph');
const method = isMorphRelation ? traverseMorphRelationTarget : traverseRelationTarget(getModel(attribute.target));
if (isArray(value)) {
const res = new Array(value.length);
for(let i = 0; i < value.length; i += 1){
const arrayPath = {
...newPath,
rawWithIndices: isNil(newPath.rawWithIndices) ? `${i}` : `${newPath.rawWithIndices}.${i}`
};
res[i] = await method(visitor, arrayPath, value[i]);
}
copy[key] = res;
} else {
copy[key] = await method(visitor, newPath, value);
}
continue;
}
if (isMediaAttribute(attribute)) {
// need to update copy
if (isArray(value)) {
const res = new Array(value.length);
for(let i = 0; i < value.length; i += 1){
const arrayPath = {
...newPath,
rawWithIndices: isNil(newPath.rawWithIndices) ? `${i}` : `${newPath.rawWithIndices}.${i}`
};
res[i] = await traverseMediaTarget(visitor, arrayPath, value[i]);
}
copy[key] = res;
} else {
copy[key] = await traverseMediaTarget(visitor, newPath, value);
}
continue;
}
if (attribute.type === 'component') {
const targetSchema = getModel(attribute.component);
if (isArray(value)) {
const res = new Array(value.length);
for(let i = 0; i < value.length; i += 1){
const arrayPath = {
...newPath,
rawWithIndices: isNil(newPath.rawWithIndices) ? `${i}` : `${newPath.rawWithIndices}.${i}`
};
res[i] = await traverseComponent(visitor, arrayPath, targetSchema, value[i]);
}
copy[key] = res;
} else {
copy[key] = await traverseComponent(visitor, newPath, targetSchema, value);
}
continue;
}
if (attribute.type === 'dynamiczone' && isArray(value)) {
const res = new Array(value.length);
for(let i = 0; i < value.length; i += 1){
const arrayPath = {
...newPath,
rawWithIndices: isNil(newPath.rawWithIndices) ? `${i}` : `${newPath.rawWithIndices}.${i}`
};
res[i] = await visitDynamicZoneEntry(visitor, arrayPath, value[i]);
}
copy[key] = res;
continue;
}
}
return copy;
};
const createVisitorUtils = ({ data })=>({
remove (key) {
delete data[key];
},
set (key, value) {
data[key] = value;
}
});
var traverseEntity$1 = curry(traverseEntity);
export { traverseEntity$1 as default };
//# sourceMappingURL=traverse-entity.mjs.map