Strapi V4 populate Media and Dynamiczones from Components

This is my version for helpers/populate.js and fits my needs … hope it helps

const { createCoreController } = require('@strapi/strapi').factories;

const DEFAULT_POPULATE = { populate: '*' };

const getPathForComponent = (componentDir, componentName) => {
  if (componentName.startsWith('api::')) {
    const parts = componentName.split('::')[1].split('.');
    return `../api/${parts[0]}/content-types/${parts[1]}/schema.json`;
  }
  return `../components/${componentDir}/${componentName}.json`;
};

const getComponentAttributes = (componentDir, componentName) => {
  try {
    return Object.entries(require(getPathForComponent(componentDir, componentName)).attributes);
  } catch (error) {
    console.error(`Error loading attributes for component: ${componentName}`, error);
    return [];
  }
};

const populateComponent = (component) => {
  const componentAttributes = getComponentAttributes(...component.split('.'));
  const attrPopulates = componentAttributes.reduce((acc, [attributeName, attribute]) => {
    return { ...acc, [attributeName]: populateAttribute(attribute) };
  }, {});
  return { ...DEFAULT_POPULATE, ...attrPopulates };
};

const populateDynamicZone = ({ components }) => {
  if (!components) return DEFAULT_POPULATE;

  return {
    populate: components.reduce((acc, component) => {
      const componentAttributes = Object.entries(require(getPathForComponent(...component.split('.'))).attributes)
        .filter(([, attr]) => ['component', 'relation', 'media', 'dynamiczone'].includes(attr.type));

      const attrPopulates = componentAttributes.reduce((innerAcc, [attributeName]) => {
        return { ...innerAcc, [attributeName]: DEFAULT_POPULATE };
      }, {});

      return { ...acc, [component.split('.').pop()]: DEFAULT_POPULATE, ...attrPopulates };
    }, {})
  };
};

const populateAttribute = (attribute) => {
  switch (attribute.type) {
    case 'component':
      return populateComponent(attribute.component);
    case 'relation':
      const componentAttributes = getComponentAttributes("", attribute.target);
      const attrPopulates = componentAttributes.reduce((acc, [attributeName, attr]) => {
        return { ...acc, [attributeName]: populateAttribute(attr) };
      }, {});
      return { ...DEFAULT_POPULATE, ...attrPopulates };
    case 'dynamiczone':
      return populateDynamicZone(attribute);
    default:
      return DEFAULT_POPULATE;
  }
};

const getPopulateFromSchema = (schema) => {
  return Object.keys(schema.attributes).reduce((acc, attributeName) => {
    return { ...acc, [attributeName]: populateAttribute(schema.attributes[attributeName]) };
  }, {});
};

function createPopulatedController(uid, schema) {
  return createCoreController(uid, () => {
    return {
      async find(ctx) {
        ctx.query = { ...ctx.query, populate: getPopulateFromSchema(schema) };
        return await super.find(ctx);
      },
      async findOne(ctx) {
        ctx.query = { ...ctx.query, populate: getPopulateFromSchema(schema) };
        return await super.findOne(ctx);
      },
    };
  });
}

module.exports = createPopulatedController;