Repository: strapi/strapi
File: packages/core/admin/admin/src/core/apis/Widgets.ts (TypeScript)

Code before:
        ? `plugin::${widgetArgs.pluginId}.${widgetArgs.id}`
        : `global::${widgetArgs.id}`
    ) as WidgetUID;
  };

  private addUidToWidgets = (widgets: (WidgetArgs | Widget)[]): Widget[] =>
    widgets.map((widget) => {
      // If widget already has uid, it's already a Widget
      if ('uid' in widget && widget.uid) {
        return widget as Widget;
      }

      // Otherwise, it's a WidgetArgs
      const widgetArgs = widget as WidgetArgs;
      invariant(widgetArgs.id, 'An id must be provided');
      invariant(widgetArgs.component, 'A component must be provided');
      invariant(widgetArgs.title, 'A title must be provided');
      invariant(widgetArgs.icon, 'An icon must be provided');

      const { id, pluginId, ...widgetWithoutId } = widgetArgs;
      return {
        ...widgetWithoutId,
        uid: this.generateUid(widgetArgs),
      };
    });

  register(widgets: WidgetArgs): void;
  register(widgets: WidgetArgs[]): void;
  register(widgets: DescriptionReducer<WidgetArgs>): void;
  register(widgets: WidgetArgs | WidgetArgs[] | DescriptionReducer<WidgetArgs>) {
    if (Array.isArray(widgets)) {
      this.widgets.push(...this.addUidToWidgets(widgets));
    } else if (typeof widgets === 'function') {
      const result = widgets(this.widgets);
      this.widgets = this.addUidToWidgets(result);
    } else if (typeof widgets === 'object') {
      this.widgets.push(this.addUidToWidgets([widgets])[0]);
    } else {
      throw new Error('Expected widgets to be an array or a reducer function');
    }
  }

  getAll = () => {
    return this.widgets;
  };
}

export { Widgets };
export type { WidgetArgs, Widget };


Review comment (suggestion):
Not sure I have the whole context here but I thought eventually we want give the control of the order of the widgets to the user. 

If so I'm not sure this is the API we want. In my opinion we should think about the interface we want to expose to the user and build that first. If the reordering thing is blocking something we want to build then all the more reason to do it now, no? Once we expose the widget register function accepting a function as an argument we are stuck with it until a major release. Personally I'm not really a fan.

Code after:
      invariant(widget.icon, 'An icon must be provided');
    });
  };

  register(widgets: WidgetArgs | WidgetArgs[] | DescriptionReducer) {
    if (Array.isArray(widgets)) {
      this.checkWidgets(widgets);
      this.widgets = [...this.widgets, ...widgets];
    } else if (typeof widgets === 'function') {
      const newWidgets = widgets(this.widgets);
      this.checkWidgets(newWidgets);
      this.widgets = newWidgets;
    } else if (typeof widgets === 'object') {
      this.checkWidgets([widgets]);
      this.widgets.push(widgets);
    } else {
      throw new Error('Expected widgets to be an array or a reducer function');
    }
  }

  getAll = (): WidgetWithUID[] => {
    return this.widgets.map((widget) => {
      const { id, pluginId, ...widgetBase } = widget;
      return {
        ...widgetBase,
        uid: this.generateUid(widget),
      };
    });
  };
}

export { Widgets };
export type { WidgetArgs, WidgetWithUID };