mirror of
https://github.com/flarum/core.git
synced 2025-07-16 06:16:23 +02:00
feat: introduce frontend extenders (#3645)
* feat: reintroduce frontend extenders * chore: used `Routes` extender in bundled extensions * chore: used `PostTypes` extender in bundled extensions * chore: `yarn format` * chore: naming * chore(review): unnecessary check * chore(review): stay consistent * chore: unused import Signed-off-by: Sami Mazouz <sychocouldy@gmail.com>
This commit is contained in:
@ -35,6 +35,7 @@ import type { ComponentAttrs } from './Component';
|
||||
import Model, { SavedModelData } from './Model';
|
||||
import fireApplicationError from './helpers/fireApplicationError';
|
||||
import IHistory from './IHistory';
|
||||
import IExtender from './extenders/IExtender';
|
||||
|
||||
export type FlarumScreens = 'phone' | 'tablet' | 'desktop' | 'desktop-hd';
|
||||
|
||||
@ -300,8 +301,7 @@ export default class Application {
|
||||
caughtInitializationErrors.forEach((handler) => handler());
|
||||
}
|
||||
|
||||
// TODO: This entire system needs a do-over for v2
|
||||
public bootExtensions(extensions: Record<string, { extend?: unknown[] }>) {
|
||||
public bootExtensions(extensions: Record<string, { extend?: IExtender[] }>) {
|
||||
Object.keys(extensions).forEach((name) => {
|
||||
const extension = extensions[name];
|
||||
|
||||
@ -311,7 +311,6 @@ export default class Application {
|
||||
const extenders = extension.extend.flat(Infinity);
|
||||
|
||||
for (const extender of extenders) {
|
||||
// @ts-expect-error This is beyond saving atm.
|
||||
extender.extend(this, { name, exports: extension });
|
||||
}
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
// @ts-expect-error We need to explicitly use the prefix to distinguish between the extend folder.
|
||||
import * as extend from './extend.ts';
|
||||
import * as extend from './extend';
|
||||
import extenders from './extenders';
|
||||
import Session from './Session';
|
||||
import Store from './Store';
|
||||
import BasicEditorDriver from './utils/BasicEditorDriver';
|
||||
@ -90,6 +90,7 @@ import ModalManagerState from './states/ModalManagerState';
|
||||
import PageState from './states/PageState';
|
||||
|
||||
export default {
|
||||
extenders,
|
||||
extend: extend,
|
||||
Session: Session,
|
||||
Store: Store,
|
||||
|
@ -1,41 +0,0 @@
|
||||
export default class Model {
|
||||
type;
|
||||
attributes = [];
|
||||
hasOnes = [];
|
||||
hasManys = [];
|
||||
|
||||
constructor(type, model = null) {
|
||||
this.type = type;
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
attribute(name) {
|
||||
this.attributes.push(name);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
hasOne(type) {
|
||||
this.hasOnes.push(type);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
hasMany(type) {
|
||||
this.hasManys.push(type);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
extend(app, extension) {
|
||||
if (this.model) {
|
||||
app.store.models[this.type] = this.model;
|
||||
}
|
||||
|
||||
const model = app.store.models[this.type];
|
||||
|
||||
this.attributes.forEach((name) => (model.prototype[name] = model.attribute(name)));
|
||||
this.hasOnes.forEach((name) => (model.prototype[name] = model.hasOne(name)));
|
||||
this.hasManys.forEach((name) => (model.prototype[name] = model.hasMany(name)));
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
export default class PostTypes {
|
||||
postComponents = {};
|
||||
|
||||
add(name, component) {
|
||||
this.postComponents[name] = component;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
extend(app, extension) {
|
||||
Object.assign(app.postComponents, this.postComponents);
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
export default class Routes {
|
||||
routes = {};
|
||||
|
||||
add(name, path, component) {
|
||||
this.routes[name] = { path, component };
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
extend(app, extension) {
|
||||
Object.assign(app.routes, this.routes);
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
export { default as Model } from './Model';
|
||||
export { default as PostTypes } from './PostTypes';
|
||||
export { default as Routes } from './Routes';
|
10
framework/core/js/src/common/extenders/IExtender.ts
Normal file
10
framework/core/js/src/common/extenders/IExtender.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import Application from '../Application';
|
||||
|
||||
export interface IExtensionModule {
|
||||
name: string;
|
||||
exports: unknown;
|
||||
}
|
||||
|
||||
export default interface IExtender {
|
||||
extend(app: Application, extension: IExtensionModule): void;
|
||||
}
|
24
framework/core/js/src/common/extenders/PostTypes.ts
Normal file
24
framework/core/js/src/common/extenders/PostTypes.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import IExtender, { IExtensionModule } from './IExtender';
|
||||
import Application from '../Application';
|
||||
import ForumApplication from '../../forum/ForumApplication';
|
||||
|
||||
export default class PostTypes implements IExtender {
|
||||
private postComponents: Record<string, any> = {};
|
||||
|
||||
/**
|
||||
* Register a new post component type.
|
||||
* Usually used for event posts.
|
||||
*
|
||||
* @param name The name of the post type.
|
||||
* @param component The component class to render the post.
|
||||
*/
|
||||
add(name: string, component: any): PostTypes {
|
||||
this.postComponents[name] = component;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
extend(app: Application, extension: IExtensionModule): void {
|
||||
Object.assign((app as unknown as ForumApplication).postComponents, this.postComponents);
|
||||
}
|
||||
}
|
33
framework/core/js/src/common/extenders/Routes.ts
Normal file
33
framework/core/js/src/common/extenders/Routes.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import Application, { FlarumGenericRoute } from '../Application';
|
||||
import IExtender, { IExtensionModule } from './IExtender';
|
||||
|
||||
type HelperRoute = (...args: any) => string;
|
||||
|
||||
export default class Routes implements IExtender {
|
||||
private routes: Record<string, FlarumGenericRoute> = {};
|
||||
private helpers: Record<string, HelperRoute> = {};
|
||||
|
||||
/**
|
||||
* Add a mithril route to the application.
|
||||
*
|
||||
* @param name The name of the route.
|
||||
* @param path The path of the route.
|
||||
* @param component must extend `Page` component.
|
||||
*/
|
||||
add(name: string, path: `/${string}`, component: any): Routes {
|
||||
this.routes[name] = { path, component };
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
helper(name: string, callback: HelperRoute): Routes {
|
||||
this.helpers[name] = callback;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
extend(app: Application, extension: IExtensionModule) {
|
||||
Object.assign(app.routes, this.routes);
|
||||
Object.assign(app.route, this.helpers);
|
||||
}
|
||||
}
|
7
framework/core/js/src/common/extenders/index.ts
Normal file
7
framework/core/js/src/common/extenders/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import PostTypes from './PostTypes';
|
||||
import Routes from './Routes';
|
||||
|
||||
export default {
|
||||
PostTypes,
|
||||
Routes,
|
||||
};
|
@ -19,10 +19,9 @@ import patchMithril from './utils/patchMithril';
|
||||
|
||||
patchMithril(window);
|
||||
|
||||
import * as Extend from './extend/index';
|
||||
import app from './app';
|
||||
|
||||
export { Extend, app };
|
||||
export { app };
|
||||
|
||||
import './utils/arrayFlatPolyfill';
|
||||
|
||||
|
Reference in New Issue
Block a user