From 47b670aa2918b4fe026e3de8b5e78bbc7d99ce18 Mon Sep 17 00:00:00 2001 From: Sami Mazouz Date: Wed, 8 Feb 2023 21:13:53 +0100 Subject: [PATCH] feat: frontend `Model` extender (#3646) * feat: reintroduce frontend extenders * chore: used `Routes` extender in bundled extensions * chore: used `PostTypes` extender in bundled extensions * chore: `yarn format` * feat: `Model` frontend extender * chore: naming * chore(review): attributes can be nullable or undefined * chore(review): delay extender implementation * chore(review): unnecessary check * chore(review): stay consistent * chore: merge conflicts * chore: unused import * chore: multiline extenders * feat: add Store extender Signed-off-by: Sami Mazouz --- .editorconfig | 2 +- extensions/flags/js/src/forum/extend.ts | 14 ++++++- extensions/flags/js/src/forum/index.ts | 9 ---- extensions/likes/js/src/forum/extend.ts | 11 ++++- extensions/likes/js/src/forum/index.js | 5 --- extensions/lock/js/src/forum/extend.ts | 10 ++++- extensions/lock/js/src/forum/index.js | 5 --- .../js/src/forum/addMentionedByList.js | 4 -- extensions/mentions/js/src/forum/extend.ts | 13 +++++- extensions/mentions/js/src/forum/index.js | 2 - extensions/nicknames/js/src/forum/extend.ts | 7 ++++ extensions/nicknames/js/src/forum/index.js | 6 +-- extensions/nicknames/js/tsconfig.json | 20 +++++++++ extensions/sticky/js/src/forum/extend.ts | 10 ++++- extensions/sticky/js/src/forum/index.js | 5 --- .../subscriptions/js/src/forum/extend.ts | 9 +++- .../subscriptions/js/src/forum/index.js | 2 - extensions/suspend/js/src/forum/extend.ts | 11 +++++ extensions/suspend/js/src/forum/index.js | 8 +--- extensions/suspend/js/tsconfig.json | 20 +++++++++ extensions/tags/js/src/admin/index.ts | 7 ++-- extensions/tags/js/src/common/extend.ts | 7 ++++ extensions/tags/js/src/forum/extend.ts | 19 +++++++-- extensions/tags/js/src/forum/index.ts | 8 ---- framework/core/js/src/common/Store.ts | 4 +- .../core/js/src/common/extenders/Model.ts | 42 +++++++++++++++++++ .../core/js/src/common/extenders/Store.ts | 23 ++++++++++ .../core/js/src/common/extenders/index.ts | 4 ++ 28 files changed, 220 insertions(+), 67 deletions(-) create mode 100644 extensions/nicknames/js/src/forum/extend.ts create mode 100644 extensions/nicknames/js/tsconfig.json create mode 100644 extensions/suspend/js/src/forum/extend.ts create mode 100644 extensions/suspend/js/tsconfig.json create mode 100644 extensions/tags/js/src/common/extend.ts create mode 100644 framework/core/js/src/common/extenders/Model.ts create mode 100644 framework/core/js/src/common/extenders/Store.ts diff --git a/.editorconfig b/.editorconfig index 8abbe8cda..1feb43fc7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -18,7 +18,7 @@ trim_trailing_whitespace = false [*.{php,xml,json}] indent_size = 4 -[tsconfig.json] +[{tsconfig.json,prettierrc.json}] indent_size = 2 [*.neon] diff --git a/extensions/flags/js/src/forum/extend.ts b/extensions/flags/js/src/forum/extend.ts index 21b191f93..3b3e8b41e 100644 --- a/extensions/flags/js/src/forum/extend.ts +++ b/extensions/flags/js/src/forum/extend.ts @@ -1,4 +1,16 @@ import Extend from 'flarum/common/extenders'; +import Post from 'flarum/common/models/Post'; import FlagsPage from './components/FlagsPage'; +import Flag from './models/Flag'; -export default [new Extend.Routes().add('flags', '/flags', FlagsPage)]; +export default [ + new Extend.Routes() // + .add('flags', '/flags', FlagsPage), + + new Extend.Store() // + .add('flags', Flag), + + new Extend.Model(Post) // + .hasMany('flags') + .attribute('canFlag'), +]; diff --git a/extensions/flags/js/src/forum/index.ts b/extensions/flags/js/src/forum/index.ts index 1cd9b4e7b..f0d4a1f0a 100644 --- a/extensions/flags/js/src/forum/index.ts +++ b/extensions/flags/js/src/forum/index.ts @@ -1,8 +1,5 @@ import app from 'flarum/forum/app'; -import Model from 'flarum/common/Model'; -import Flag from './models/Flag'; -import FlagsPage from './components/FlagsPage'; import FlagListState from './states/FlagListState'; import addFlagControl from './addFlagControl'; import addFlagsDropdown from './addFlagsDropdown'; @@ -11,11 +8,6 @@ import addFlagsToPosts from './addFlagsToPosts'; export { default as extend } from './extend'; app.initializers.add('flarum-flags', () => { - Post.prototype.flags = Model.hasMany('flags'); - Post.prototype.canFlag = Model.attribute('canFlag'); - - app.store.models.flags = Flag; - app.flags = new FlagListState(app); addFlagControl(); @@ -26,6 +18,5 @@ app.initializers.add('flarum-flags', () => { // Expose compat API import flagsCompat from './compat'; import { compat } from '@flarum/core/forum'; -import Post from 'flarum/common/models/Post'; Object.assign(compat, flagsCompat); diff --git a/extensions/likes/js/src/forum/extend.ts b/extensions/likes/js/src/forum/extend.ts index d9171d936..6a95fc378 100644 --- a/extensions/likes/js/src/forum/extend.ts +++ b/extensions/likes/js/src/forum/extend.ts @@ -1,4 +1,13 @@ import Extend from 'flarum/common/extenders'; +import Post from 'flarum/common/models/Post'; +import User from 'flarum/common/models/User'; import LikesUserPage from './components/LikesUserPage'; -export default [new Extend.Routes().add('user.likes', '/u/:username/likes', LikesUserPage)]; +export default [ + new Extend.Routes() // + .add('user.likes', '/u/:username/likes', LikesUserPage), + + new Extend.Model(Post) // + .hasMany('likes') + .attribute('canLike'), +]; diff --git a/extensions/likes/js/src/forum/index.js b/extensions/likes/js/src/forum/index.js index d36b5f082..29d7a1d65 100644 --- a/extensions/likes/js/src/forum/index.js +++ b/extensions/likes/js/src/forum/index.js @@ -1,7 +1,5 @@ import { extend } from 'flarum/common/extend'; import app from 'flarum/forum/app'; -import Post from 'flarum/common/models/Post'; -import Model from 'flarum/common/Model'; import NotificationGrid from 'flarum/forum/components/NotificationGrid'; import addLikeAction from './addLikeAction'; @@ -14,9 +12,6 @@ export { default as extend } from './extend'; app.initializers.add('flarum-likes', () => { app.notificationComponents.postLiked = PostLikedNotification; - Post.prototype.canLike = Model.attribute('canLike'); - Post.prototype.likes = Model.hasMany('likes'); - addLikeAction(); addLikesList(); addLikesTabToUserProfile(); diff --git a/extensions/lock/js/src/forum/extend.ts b/extensions/lock/js/src/forum/extend.ts index ab717a167..2e510e105 100644 --- a/extensions/lock/js/src/forum/extend.ts +++ b/extensions/lock/js/src/forum/extend.ts @@ -1,4 +1,12 @@ import Extend from 'flarum/common/extenders'; +import Discussion from 'flarum/common/models/Discussion'; import DiscussionLockedPost from './components/DiscussionLockedPost'; -export default [new Extend.PostTypes().add('discussionLocked', DiscussionLockedPost)]; +export default [ + new Extend.PostTypes() // + .add('discussionLocked', DiscussionLockedPost), + + new Extend.Model(Discussion) // + .attribute('isLocked') + .attribute('canLock'), +]; diff --git a/extensions/lock/js/src/forum/index.js b/extensions/lock/js/src/forum/index.js index 7e02ca761..0ac405d3c 100644 --- a/extensions/lock/js/src/forum/index.js +++ b/extensions/lock/js/src/forum/index.js @@ -1,7 +1,5 @@ import { extend } from 'flarum/common/extend'; import app from 'flarum/forum/app'; -import Model from 'flarum/common/Model'; -import Discussion from 'flarum/common/models/Discussion'; import NotificationGrid from 'flarum/forum/components/NotificationGrid'; import DiscussionLockedNotification from './components/DiscussionLockedNotification'; @@ -13,9 +11,6 @@ export { default as extend } from './extend'; app.initializers.add('flarum-lock', () => { app.notificationComponents.discussionLocked = DiscussionLockedNotification; - Discussion.prototype.isLocked = Model.attribute('isLocked'); - Discussion.prototype.canLock = Model.attribute('canLock'); - addLockBadge(); addLockControl(); diff --git a/extensions/mentions/js/src/forum/addMentionedByList.js b/extensions/mentions/js/src/forum/addMentionedByList.js index 63e02de01..737621869 100644 --- a/extensions/mentions/js/src/forum/addMentionedByList.js +++ b/extensions/mentions/js/src/forum/addMentionedByList.js @@ -1,7 +1,5 @@ import app from 'flarum/forum/app'; import { extend } from 'flarum/common/extend'; -import Model from 'flarum/common/Model'; -import Post from 'flarum/common/models/Post'; import CommentPost from 'flarum/forum/components/CommentPost'; import Link from 'flarum/common/components/Link'; import PostPreview from 'flarum/forum/components/PostPreview'; @@ -10,8 +8,6 @@ import username from 'flarum/common/helpers/username'; import icon from 'flarum/common/helpers/icon'; export default function addMentionedByList() { - Post.prototype.mentionedBy = Model.hasMany('mentionedBy'); - function hidePreview() { this.$('.Post-mentionedBy-preview') .removeClass('in') diff --git a/extensions/mentions/js/src/forum/extend.ts b/extensions/mentions/js/src/forum/extend.ts index d860db4fd..d98a9613f 100644 --- a/extensions/mentions/js/src/forum/extend.ts +++ b/extensions/mentions/js/src/forum/extend.ts @@ -1,4 +1,15 @@ import Extend from 'flarum/common/extenders'; +import Post from 'flarum/common/models/Post'; +import User from 'flarum/common/models/User'; import MentionsUserPage from './components/MentionsUserPage'; -export default [new Extend.Routes().add('user.mentions', '/u/:username/mentions', MentionsUserPage)]; +export default [ + new Extend.Routes() // + .add('user.mentions', '/u/:username/mentions', MentionsUserPage), + + new Extend.Model(Post) // + .hasMany('mentionedBy'), + + new Extend.Model(User) // + .attribute('canMentionGroups'), +]; diff --git a/extensions/mentions/js/src/forum/index.js b/extensions/mentions/js/src/forum/index.js index 96c9caed1..40910b656 100644 --- a/extensions/mentions/js/src/forum/index.js +++ b/extensions/mentions/js/src/forum/index.js @@ -21,8 +21,6 @@ import Model from 'flarum/common/Model'; export { default as extend } from './extend'; app.initializers.add('flarum-mentions', function () { - User.prototype.canMentionGroups = Model.attribute('canMentionGroups'); - // For every mention of a post inside a post's content, set up a hover handler // that shows a preview of the mentioned post. addPostMentionPreviews(); diff --git a/extensions/nicknames/js/src/forum/extend.ts b/extensions/nicknames/js/src/forum/extend.ts new file mode 100644 index 000000000..373a9f949 --- /dev/null +++ b/extensions/nicknames/js/src/forum/extend.ts @@ -0,0 +1,7 @@ +import Extend from 'flarum/common/extenders'; +import User from 'flarum/common/models/User'; + +export default [ + new Extend.Model(User) // + .attribute('canEditNickname'), +]; diff --git a/extensions/nicknames/js/src/forum/index.js b/extensions/nicknames/js/src/forum/index.js index 65f6ae95f..8c353494c 100644 --- a/extensions/nicknames/js/src/forum/index.js +++ b/extensions/nicknames/js/src/forum/index.js @@ -4,15 +4,13 @@ import Button from 'flarum/common/components/Button'; import EditUserModal from 'flarum/common/components/EditUserModal'; import SignUpModal from 'flarum/forum/components/SignUpModal'; import SettingsPage from 'flarum/forum/components/SettingsPage'; -import Model from 'flarum/common/Model'; -import User from 'flarum/common/models/User'; import extractText from 'flarum/common/utils/extractText'; import Stream from 'flarum/common/utils/Stream'; import NickNameModal from './components/NicknameModal'; -app.initializers.add('flarum/nicknames', () => { - User.prototype.canEditNickname = Model.attribute('canEditNickname'); +export { default as extend } from './extend'; +app.initializers.add('flarum/nicknames', () => { extend(SettingsPage.prototype, 'accountItems', function (items) { if (app.forum.attribute('displayNameDriver') !== 'nickname') return; diff --git a/extensions/nicknames/js/tsconfig.json b/extensions/nicknames/js/tsconfig.json new file mode 100644 index 000000000..f427c289e --- /dev/null +++ b/extensions/nicknames/js/tsconfig.json @@ -0,0 +1,20 @@ +{ + // Use Flarum's tsconfig as a starting point + "extends": "flarum-tsconfig", + // This will match all .ts, .tsx, .d.ts, .js, .jsx files in your `src` folder + // and also tells your Typescript server to read core's global typings for + // access to `dayjs` and `$` in the global namespace. + "include": ["src/**/*", "../../../framework/core/js/dist-typings/@types/**/*", "@types/**/*"], + "compilerOptions": { + // This will output typings to `dist-typings` + "declarationDir": "./dist-typings", + "paths": { + "flarum/*": ["../../../framework/core/js/dist-typings/*"], + // TODO: remove after export registry system implemented + // Without this, the old-style `@flarum/core` import is resolved to + // source code in flarum/core instead of the dist typings. + // This causes an inaccurate "duplicate export" error. + "@flarum/core/*": ["../../../framework/core/js/dist-typings/*"], + } + } +} diff --git a/extensions/sticky/js/src/forum/extend.ts b/extensions/sticky/js/src/forum/extend.ts index 1e13921ec..c3a73c859 100644 --- a/extensions/sticky/js/src/forum/extend.ts +++ b/extensions/sticky/js/src/forum/extend.ts @@ -1,4 +1,12 @@ import Extend from 'flarum/common/extenders'; +import Discussion from 'flarum/common/models/Discussion'; import DiscussionStickiedPost from './components/DiscussionStickiedPost'; -export default [new Extend.PostTypes().add('discussionStickied', DiscussionStickiedPost)]; +export default [ + new Extend.PostTypes() // + .add('discussionStickied', DiscussionStickiedPost), + + new Extend.Model(Discussion) // + .attribute('isSticky') + .attribute('canSticky'), +]; diff --git a/extensions/sticky/js/src/forum/index.js b/extensions/sticky/js/src/forum/index.js index d42713a93..be902a403 100644 --- a/extensions/sticky/js/src/forum/index.js +++ b/extensions/sticky/js/src/forum/index.js @@ -1,6 +1,4 @@ import app from 'flarum/forum/app'; -import Model from 'flarum/common/Model'; -import Discussion from 'flarum/common/models/Discussion'; import addStickyBadge from './addStickyBadge'; import addStickyControl from './addStickyControl'; @@ -10,9 +8,6 @@ import addStickyClass from './addStickyClass'; export { default as extend } from './extend'; app.initializers.add('flarum-sticky', () => { - Discussion.prototype.isSticky = Model.attribute('isSticky'); - Discussion.prototype.canSticky = Model.attribute('canSticky'); - addStickyBadge(); addStickyControl(); addStickyExcerpt(); diff --git a/extensions/subscriptions/js/src/forum/extend.ts b/extensions/subscriptions/js/src/forum/extend.ts index 6caf9a48d..18a948b21 100644 --- a/extensions/subscriptions/js/src/forum/extend.ts +++ b/extensions/subscriptions/js/src/forum/extend.ts @@ -1,4 +1,11 @@ import Extend from 'flarum/common/extenders'; import IndexPage from 'flarum/forum/components/IndexPage'; +import Discussion from 'flarum/common/models/Discussion'; -export default [new Extend.Routes().add('following', '/following', IndexPage)]; +export default [ + new Extend.Routes() // + .add('following', '/following', IndexPage), + + new Extend.Model(Discussion) // + .attribute('subscription'), +]; diff --git a/extensions/subscriptions/js/src/forum/index.js b/extensions/subscriptions/js/src/forum/index.js index 091b61462..181fa4dc9 100644 --- a/extensions/subscriptions/js/src/forum/index.js +++ b/extensions/subscriptions/js/src/forum/index.js @@ -16,8 +16,6 @@ export { default as extend } from './extend'; app.initializers.add('subscriptions', function () { app.notificationComponents.newPost = NewPostNotification; - Discussion.prototype.subscription = Model.attribute('subscription'); - addSubscriptionBadge(); addSubscriptionControls(); addSubscriptionFilter(); diff --git a/extensions/suspend/js/src/forum/extend.ts b/extensions/suspend/js/src/forum/extend.ts new file mode 100644 index 000000000..6fd80fc86 --- /dev/null +++ b/extensions/suspend/js/src/forum/extend.ts @@ -0,0 +1,11 @@ +import Extend from 'flarum/common/extenders'; +import User from 'flarum/common/models/User'; +import Model from 'flarum/common/Model'; + +export default [ + new Extend.Model(User) + .attribute('canSuspend') + .attribute('suspendedUntil', Model.transformDate) + .attribute('suspendReason') + .attribute('suspendMessage'), +]; diff --git a/extensions/suspend/js/src/forum/index.js b/extensions/suspend/js/src/forum/index.js index 93bc82856..62c6b9b25 100644 --- a/extensions/suspend/js/src/forum/index.js +++ b/extensions/suspend/js/src/forum/index.js @@ -3,7 +3,6 @@ import app from 'flarum/app'; import UserControls from 'flarum/utils/UserControls'; import Button from 'flarum/components/Button'; import Badge from 'flarum/components/Badge'; -import Model from 'flarum/Model'; import User from 'flarum/models/User'; import SuspendUserModal from './components/SuspendUserModal'; @@ -11,15 +10,12 @@ import UserSuspendedNotification from './components/UserSuspendedNotification'; import UserUnsuspendedNotification from './components/UserUnsuspendedNotification'; import checkForSuspension from './checkForSuspension'; +export { default as extend } from './extend'; + app.initializers.add('flarum-suspend', () => { app.notificationComponents.userSuspended = UserSuspendedNotification; app.notificationComponents.userUnsuspended = UserUnsuspendedNotification; - User.prototype.canSuspend = Model.attribute('canSuspend'); - User.prototype.suspendedUntil = Model.attribute('suspendedUntil', Model.transformDate); - User.prototype.suspendReason = Model.attribute('suspendReason'); - User.prototype.suspendMessage = Model.attribute('suspendMessage'); - extend(UserControls, 'moderationControls', (items, user) => { if (user.canSuspend()) { items.add( diff --git a/extensions/suspend/js/tsconfig.json b/extensions/suspend/js/tsconfig.json new file mode 100644 index 000000000..f427c289e --- /dev/null +++ b/extensions/suspend/js/tsconfig.json @@ -0,0 +1,20 @@ +{ + // Use Flarum's tsconfig as a starting point + "extends": "flarum-tsconfig", + // This will match all .ts, .tsx, .d.ts, .js, .jsx files in your `src` folder + // and also tells your Typescript server to read core's global typings for + // access to `dayjs` and `$` in the global namespace. + "include": ["src/**/*", "../../../framework/core/js/dist-typings/@types/**/*", "@types/**/*"], + "compilerOptions": { + // This will output typings to `dist-typings` + "declarationDir": "./dist-typings", + "paths": { + "flarum/*": ["../../../framework/core/js/dist-typings/*"], + // TODO: remove after export registry system implemented + // Without this, the old-style `@flarum/core` import is resolved to + // source code in flarum/core instead of the dist typings. + // This causes an inaccurate "duplicate export" error. + "@flarum/core/*": ["../../../framework/core/js/dist-typings/*"], + } + } +} diff --git a/extensions/tags/js/src/admin/index.ts b/extensions/tags/js/src/admin/index.ts index 99429068e..73ba090fc 100644 --- a/extensions/tags/js/src/admin/index.ts +++ b/extensions/tags/js/src/admin/index.ts @@ -1,15 +1,15 @@ import app from 'flarum/admin/app'; -import Tag from '../common/models/Tag'; import addTagsPermissionScope from './addTagsPermissionScope'; import addTagPermission from './addTagPermission'; import addTagsHomePageOption from './addTagsHomePageOption'; import addTagChangePermission from './addTagChangePermission'; +import addTagSelectionSettingComponent from './addTagSelectionSettingComponent'; import TagsPage from './components/TagsPage'; import TagListState from '../common/states/TagListState'; -app.initializers.add('flarum-tags', (app) => { - app.store.models.tags = Tag; +export { default as extend } from '../common/extend'; +app.initializers.add('flarum-tags', (app) => { app.tagList = new TagListState(); app.extensionData.for('flarum-tags').registerPage(TagsPage); @@ -24,6 +24,5 @@ app.initializers.add('flarum-tags', (app) => { // Expose compat API import tagsCompat from './compat'; import { compat } from '@flarum/core/admin'; -import addTagSelectionSettingComponent from './addTagSelectionSettingComponent'; Object.assign(compat, tagsCompat); diff --git a/extensions/tags/js/src/common/extend.ts b/extensions/tags/js/src/common/extend.ts new file mode 100644 index 000000000..ca8be1f80 --- /dev/null +++ b/extensions/tags/js/src/common/extend.ts @@ -0,0 +1,7 @@ +import Extend from 'flarum/common/extenders'; +import Tag from './models/Tag'; + +export default [ + new Extend.Store() // + .add('tags', Tag), +]; diff --git a/extensions/tags/js/src/forum/extend.ts b/extensions/tags/js/src/forum/extend.ts index 40191cd3f..c34a8c559 100644 --- a/extensions/tags/js/src/forum/extend.ts +++ b/extensions/tags/js/src/forum/extend.ts @@ -1,14 +1,25 @@ import app from 'flarum/forum/app'; import Extend from 'flarum/common/extenders'; import IndexPage from 'flarum/forum/components/IndexPage'; +import Discussion from 'flarum/common/models/Discussion'; import DiscussionTaggedPost from './components/DiscussionTaggedPost'; import TagsPage from './components/TagsPage'; +import Tag from '../common/models/Tag'; + +import commonExtend from '../common/extend'; export default [ - new Extend.Routes() - .add('tags', '/tags', TagsPage) - .add('tag', '/t/:tags', IndexPage) + ...commonExtend, + + new Extend.Routes() // + .add('tags', '/tags', TagsPage) // + .add('tag', '/t/:tags', IndexPage) // .helper('tag', (tag) => app.route('tag', { tags: tag.slug() })), - new Extend.PostTypes().add('discussionTagged', DiscussionTaggedPost), + new Extend.PostTypes() // + .add('discussionTagged', DiscussionTaggedPost), + + new Extend.Model(Discussion) // + .hasMany('tags') // + .attribute('canTag'), ]; diff --git a/extensions/tags/js/src/forum/index.ts b/extensions/tags/js/src/forum/index.ts index 4822863b7..838f66948 100644 --- a/extensions/tags/js/src/forum/index.ts +++ b/extensions/tags/js/src/forum/index.ts @@ -1,9 +1,6 @@ import app from 'flarum/forum/app'; -import Model from 'flarum/common/Model'; -import Discussion from 'flarum/common/models/Discussion'; import TagListState from '../common/states/TagListState'; -import Tag from '../common/models/Tag'; import addTagList from './addTagList'; import addTagFilter from './addTagFilter'; @@ -14,13 +11,8 @@ import addTagComposer from './addTagComposer'; export { default as extend } from './extend'; app.initializers.add('flarum-tags', function () { - app.store.models.tags = Tag; - app.tagList = new TagListState(); - Discussion.prototype.tags = Model.hasMany('tags'); - Discussion.prototype.canTag = Model.attribute('canTag'); - addTagList(); addTagFilter(); addTagLabels(); diff --git a/framework/core/js/src/common/Store.ts b/framework/core/js/src/common/Store.ts index 65fd4b693..0293a2f79 100644 --- a/framework/core/js/src/common/Store.ts +++ b/framework/core/js/src/common/Store.ts @@ -83,9 +83,9 @@ export default class Store { * The model registry. A map of resource types to the model class that * should be used to represent resources of that type. */ - models: Record; + models: Record; - constructor(models: Record) { + constructor(models: Record) { this.models = models; } diff --git a/framework/core/js/src/common/extenders/Model.ts b/framework/core/js/src/common/extenders/Model.ts new file mode 100644 index 000000000..2416bc474 --- /dev/null +++ b/framework/core/js/src/common/extenders/Model.ts @@ -0,0 +1,42 @@ +import IExtender, { IExtensionModule } from './IExtender'; +import Application from '../Application'; +import ActualModel from '../Model'; + +export default class Model implements IExtender { + private readonly model: { new (): ActualModel }; + private callbacks: Array<() => void> = []; + + public constructor(model: { new (): ActualModel }) { + this.model = model; + } + + public attribute(name: string, transform: ((attr: O) => T) | null = null): Model { + this.callbacks.push(() => { + this.model.prototype[name] = transform ? ActualModel.attribute(name, transform) : ActualModel.attribute(name); + }); + + return this; + } + + public hasOne(name: string): Model { + this.callbacks.push(() => { + this.model.prototype[name] = ActualModel.hasOne(name); + }); + + return this; + } + + public hasMany(name: string): Model { + this.callbacks.push(() => { + this.model.prototype[name] = ActualModel.hasMany(name); + }); + + return this; + } + + extend(app: Application, extension: IExtensionModule): void { + for (const callback of this.callbacks) { + callback.call(this); + } + } +} diff --git a/framework/core/js/src/common/extenders/Store.ts b/framework/core/js/src/common/extenders/Store.ts new file mode 100644 index 000000000..2b0f7fae8 --- /dev/null +++ b/framework/core/js/src/common/extenders/Store.ts @@ -0,0 +1,23 @@ +import Application from '../Application'; +import IExtender, { IExtensionModule } from './IExtender'; +import Model from '../Model'; + +export default class Store implements IExtender { + private readonly models: { [type: string]: { new (): Model } } = {}; + + public add(type: string, model: { new (): Model }): Store { + this.models[type] = model; + + return this; + } + + extend(app: Application, extension: IExtensionModule): void { + for (const type in this.models) { + if (app.store.models[type]) { + throw new Error(`The model type "${type}" has already been registered with the class "${app.store.models[type].name}".`); + } + + app.store.models[type] = this.models[type]; + } + } +} diff --git a/framework/core/js/src/common/extenders/index.ts b/framework/core/js/src/common/extenders/index.ts index 58fe4f303..8a4e58cb9 100644 --- a/framework/core/js/src/common/extenders/index.ts +++ b/framework/core/js/src/common/extenders/index.ts @@ -1,7 +1,11 @@ +import Model from './Model'; import PostTypes from './PostTypes'; import Routes from './Routes'; +import Store from './Store'; export default { + Model, PostTypes, Routes, + Store, };