mirror of
https://github.com/flarum/core.git
synced 2025-07-25 02:31:17 +02:00
[WIP] JS Extender API foundation (#1468)
* Run extenders exported by extensions * Add some basic extenders * Patch Mithril as the very first thing so extension code can run safely * Load the payload into the app before booting extensions * Setup default routes before booting extensions
This commit is contained in:
5
framework/core/js/package-lock.json
generated
5
framework/core/js/package-lock.json
generated
@@ -5153,6 +5153,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
|
||||||
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
|
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
|
||||||
},
|
},
|
||||||
|
"lodash-es": {
|
||||||
|
"version": "4.17.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.10.tgz",
|
||||||
|
"integrity": "sha512-iesFYPmxYYGTcmQK0sL8bX3TGHyM6b2qREaB4kamHfQyfPJP0xgoGxp19nsH16nsfquLdiyKyX3mQkfiSGV8Rg=="
|
||||||
|
},
|
||||||
"log-symbols": {
|
"log-symbols": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
"flarum-webpack-config": "^0.1.0-beta.8",
|
"flarum-webpack-config": "^0.1.0-beta.8",
|
||||||
"jquery": "^3.3.1",
|
"jquery": "^3.3.1",
|
||||||
"jquery.hotkeys": "^0.1.0",
|
"jquery.hotkeys": "^0.1.0",
|
||||||
|
"lodash-es": "^4.17.10",
|
||||||
"m.attrs.bidi": "github:tobscure/m.attrs.bidi",
|
"m.attrs.bidi": "github:tobscure/m.attrs.bidi",
|
||||||
"mithril": "^0.2.8",
|
"mithril": "^0.2.8",
|
||||||
"moment": "^2.22.2",
|
"moment": "^2.22.2",
|
||||||
|
@@ -17,13 +17,10 @@ export default class AdminApplication extends Application {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
constructor() {
|
||||||
* @inheritdoc
|
super();
|
||||||
*/
|
|
||||||
boot(data) {
|
|
||||||
routes(this);
|
|
||||||
|
|
||||||
super.boot(data);
|
routes(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,13 +1,3 @@
|
|||||||
import 'expose-loader?$!expose-loader?jQuery!jquery';
|
|
||||||
import 'expose-loader?m!mithril';
|
|
||||||
import 'expose-loader?moment!moment';
|
|
||||||
import 'expose-loader?m.bidi!m.attrs.bidi';
|
|
||||||
import 'bootstrap/js/affix';
|
|
||||||
import 'bootstrap/js/dropdown';
|
|
||||||
import 'bootstrap/js/modal';
|
|
||||||
import 'bootstrap/js/tooltip';
|
|
||||||
import 'bootstrap/js/transition';
|
|
||||||
|
|
||||||
import AdminApplication from './AdminApplication';
|
import AdminApplication from './AdminApplication';
|
||||||
|
|
||||||
const app = new AdminApplication();
|
const app = new AdminApplication();
|
||||||
|
@@ -10,7 +10,6 @@ import Session from './Session';
|
|||||||
import extract from './utils/extract';
|
import extract from './utils/extract';
|
||||||
import Drawer from './utils/Drawer';
|
import Drawer from './utils/Drawer';
|
||||||
import mapRoutes from './utils/mapRoutes';
|
import mapRoutes from './utils/mapRoutes';
|
||||||
import patchMithril from './utils/patchMithril';
|
|
||||||
import RequestError from './utils/RequestError';
|
import RequestError from './utils/RequestError';
|
||||||
import ScrollListener from './utils/ScrollListener';
|
import ScrollListener from './utils/ScrollListener';
|
||||||
import { extend } from './extend';
|
import { extend } from './extend';
|
||||||
@@ -21,6 +20,7 @@ import Discussion from './models/Discussion';
|
|||||||
import Post from './models/Post';
|
import Post from './models/Post';
|
||||||
import Group from './models/Group';
|
import Group from './models/Group';
|
||||||
import Notification from './models/Notification';
|
import Notification from './models/Notification';
|
||||||
|
import { flattenDeep } from 'lodash-es';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `App` class provides a container for an application, as well as various
|
* The `App` class provides a container for an application, as well as various
|
||||||
@@ -115,16 +115,17 @@ export default class Application {
|
|||||||
*/
|
*/
|
||||||
requestError = null;
|
requestError = null;
|
||||||
|
|
||||||
|
data;
|
||||||
|
|
||||||
title = '';
|
title = '';
|
||||||
titleCount = 0;
|
titleCount = 0;
|
||||||
|
|
||||||
boot(data) {
|
load(payload) {
|
||||||
this.data = data;
|
this.data = payload;
|
||||||
|
this.translator.locale = payload.locale;
|
||||||
this.translator.locale = data.locale;
|
}
|
||||||
|
|
||||||
patchMithril(window);
|
|
||||||
|
|
||||||
|
boot() {
|
||||||
this.initializers.toArray().forEach(initializer => initializer(this));
|
this.initializers.toArray().forEach(initializer => initializer(this));
|
||||||
|
|
||||||
this.store.pushPayload({data: this.data.resources});
|
this.store.pushPayload({data: this.data.resources});
|
||||||
@@ -139,6 +140,18 @@ export default class Application {
|
|||||||
this.mount();
|
this.mount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bootExtensions(extensions) {
|
||||||
|
Object.keys(extensions).forEach(name => {
|
||||||
|
const extension = extensions[name];
|
||||||
|
|
||||||
|
const extenders = flattenDeep(extension.extend);
|
||||||
|
|
||||||
|
for (const extender of extenders) {
|
||||||
|
extender.extend(this, { name, exports: extension });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
mount() {
|
mount() {
|
||||||
this.modal = m.mount(document.getElementById('modal'), <ModalManager/>);
|
this.modal = m.mount(document.getElementById('modal'), <ModalManager/>);
|
||||||
this.alerts = m.mount(document.getElementById('alerts'), <AlertManager/>);
|
this.alerts = m.mount(document.getElementById('alerts'), <AlertManager/>);
|
||||||
|
41
framework/core/js/src/common/extend/Model.js
Normal file
41
framework/core/js/src/common/extend/Model.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
export default class Routes {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
13
framework/core/js/src/common/extend/PostTypes.js
Normal file
13
framework/core/js/src/common/extend/PostTypes.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
export default class PostTypes {
|
||||||
|
postComponents = {};
|
||||||
|
|
||||||
|
add(name, component) {
|
||||||
|
this.postComponents[name] = component;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
extend(app, extension) {
|
||||||
|
Object.assign(app.postComponents, this.postComponents);
|
||||||
|
}
|
||||||
|
}
|
13
framework/core/js/src/common/extend/Routes.js
Normal file
13
framework/core/js/src/common/extend/Routes.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
3
framework/core/js/src/common/extend/index.js
Normal file
3
framework/core/js/src/common/extend/index.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export { default as Model } from './Model';
|
||||||
|
export { default as PostTypes } from './PostTypes';
|
||||||
|
export { default as Routes } from './Routes';
|
@@ -0,0 +1,18 @@
|
|||||||
|
import 'expose-loader?$!expose-loader?jQuery!jquery';
|
||||||
|
import 'expose-loader?m!mithril';
|
||||||
|
import 'expose-loader?moment!moment';
|
||||||
|
import 'expose-loader?m.bidi!m.attrs.bidi';
|
||||||
|
import 'bootstrap/js/affix';
|
||||||
|
import 'bootstrap/js/dropdown';
|
||||||
|
import 'bootstrap/js/modal';
|
||||||
|
import 'bootstrap/js/tooltip';
|
||||||
|
import 'bootstrap/js/transition';
|
||||||
|
import 'jquery.hotkeys/jquery.hotkeys';
|
||||||
|
|
||||||
|
import patchMithril from './utils/patchMithril';
|
||||||
|
|
||||||
|
patchMithril(window);
|
||||||
|
|
||||||
|
import * as Extend from './extend/index';
|
||||||
|
|
||||||
|
export { Extend };
|
||||||
|
@@ -63,13 +63,10 @@ export default class ForumApplication extends Application {
|
|||||||
*/
|
*/
|
||||||
history = new History();
|
history = new History();
|
||||||
|
|
||||||
/**
|
constructor() {
|
||||||
* @inheritdoc
|
super();
|
||||||
*/
|
|
||||||
boot(data) {
|
|
||||||
routes(this);
|
|
||||||
|
|
||||||
super.boot(data);
|
routes(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,15 +1,5 @@
|
|||||||
import 'expose-loader?$!expose-loader?jQuery!jquery';
|
|
||||||
import 'expose-loader?m!mithril';
|
|
||||||
import 'expose-loader?moment!moment';
|
|
||||||
import 'expose-loader?punycode!punycode';
|
import 'expose-loader?punycode!punycode';
|
||||||
import 'expose-loader?ColorThief!color-thief-browser';
|
import 'expose-loader?ColorThief!color-thief-browser';
|
||||||
import 'expose-loader?m.bidi!m.attrs.bidi';
|
|
||||||
import 'bootstrap/js/affix';
|
|
||||||
import 'bootstrap/js/dropdown';
|
|
||||||
import 'bootstrap/js/modal';
|
|
||||||
import 'bootstrap/js/tooltip';
|
|
||||||
import 'bootstrap/js/transition';
|
|
||||||
import 'jquery.hotkeys/jquery.hotkeys';
|
|
||||||
|
|
||||||
import ForumApplication from './ForumApplication';
|
import ForumApplication from './ForumApplication';
|
||||||
|
|
||||||
|
@@ -57,7 +57,7 @@ class Assets implements ExtenderInterface
|
|||||||
$event->view->getJs()->addString(function () use ($extension) {
|
$event->view->getJs()->addString(function () use ($extension) {
|
||||||
$name = $extension->getId();
|
$name = $extension->getId();
|
||||||
|
|
||||||
return 'var module={};'.file_get_contents($this->js).";flarum.extensions['$name']=module.exports";
|
return 'var module={};'.file_get_contents($this->js).";\nflarum.extensions['$name']=module.exports";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,7 @@
|
|||||||
@if ($allowJs)
|
@if ($allowJs)
|
||||||
<script>
|
<script>
|
||||||
document.getElementById('flarum-loading').style.display = 'block';
|
document.getElementById('flarum-loading').style.display = 'block';
|
||||||
var flarum={extensions:{}};
|
var flarum = {extensions: {}};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@foreach ($jsUrls as $url)
|
@foreach ($jsUrls as $url)
|
||||||
@@ -42,7 +42,9 @@
|
|||||||
@if (! $debug)
|
@if (! $debug)
|
||||||
try {
|
try {
|
||||||
@endif
|
@endif
|
||||||
flarum.core.app.boot(@json($payload));
|
flarum.core.app.load(@json($payload));
|
||||||
|
flarum.core.app.bootExtensions(flarum.extensions);
|
||||||
|
flarum.core.app.boot();
|
||||||
@if (! $debug)
|
@if (! $debug)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
window.location += (window.location.search ? '&' : '?') + 'nojs=1';
|
window.location += (window.location.search ? '&' : '?') + 'nojs=1';
|
||||||
|
Reference in New Issue
Block a user