From 9d48c4751a7c025e894a9eba5b181c2e25cd25f0 Mon Sep 17 00:00:00 2001 From: Kyle Shockey Date: Thu, 28 Dec 2017 16:26:05 -0600 Subject: [PATCH] Refactor `afterLoad` interface to expose raw plugin context --- docs/customization/plugin-api.md | 11 ++-- src/core/plugins/auth/index.js | 3 +- src/core/system.js | 31 +++++++++--- test/core/system/system.js | 87 +++++++++++++++++++++++++++++++- 4 files changed, 118 insertions(+), 14 deletions(-) diff --git a/docs/customization/plugin-api.md b/docs/customization/plugin-api.md index ee08ba07..54d62583 100644 --- a/docs/customization/plugin-api.md +++ b/docs/customization/plugin-api.md @@ -19,7 +19,7 @@ A plugin return value may contain any of these keys, where `myStateKey` is a nam }, components: {}, wrapComponents: {}, - afterLoad: (system) => {} + afterLoad: (system) => {}, fn: {}, } ``` @@ -366,9 +366,11 @@ const MyWrapComponentPlugin = function(system) { ##### `afterLoad` -The `afterLoad` plugin method allows you to get a reference to the system after your plugin has been registered with the system. +The `afterLoad` plugin method allows you to get a reference to the system after your plugin has been registered. -This interface is used in the core code to attach methods that are driven by bound selectors or actions directly to the system. +This interface is used in the core code to attach methods that are driven by bound selectors or actions directly to the plugin context. + +The plugin context, which is bound to `this`, is undocumented, but below is an example of how to attach a bound action as a top-level method: ```javascript const MyMethodProvidingPlugin = function() { @@ -376,7 +378,8 @@ const MyMethodProvidingPlugin = function() { afterLoad(system) { // at this point in time, your actions have been bound into the system // so you can do things with them - system.myMethod = system.exampleActions.updateFavoriteColor + this.rootInjects = this.rootInjects || {} + this.rootInjects.myMethod = system.exampleActions.updateFavoriteColor }, statePlugins: { example: { diff --git a/src/core/plugins/auth/index.js b/src/core/plugins/auth/index.js index 7e9413f4..3ac5e1ff 100644 --- a/src/core/plugins/auth/index.js +++ b/src/core/plugins/auth/index.js @@ -6,7 +6,8 @@ import * as specWrapActionReplacements from "./spec-wrap-actions" export default function() { return { afterLoad(system) { - system.initOAuth = system.authActions.configureAuth + this.rootInjects = this.rootInjects || {} + this.rootInjects.initOAuth = system.authActions.configureAuth }, statePlugins: { auth: { diff --git a/src/core/system.js b/src/core/system.js index 87a25887..73c602a8 100644 --- a/src/core/system.js +++ b/src/core/system.js @@ -68,13 +68,11 @@ export default class Store { if(rebuild) { this.buildSystem() } - - if(Array.isArray(plugins)) { - plugins.forEach(plugin => { - if(plugin.afterLoad) { - plugin.afterLoad(this.getSystem()) - } - }) + + const needAnotherRebuild = callAfterLoad.call(this.system, plugins, this.getSystem()) + + if(needAnotherRebuild) { + this.buildSystem() } } @@ -328,6 +326,25 @@ function combinePlugins(plugins, toolbox) { return {} } +function callAfterLoad(plugins, system, { hasLoaded } = {}) { + let calledSomething = hasLoaded + if(isObject(plugins) && !isArray(plugins)) { + if(typeof plugins.afterLoad === "function") { + calledSomething = true + plugins.afterLoad.call(this, system) + } + } + + if(isFunc(plugins)) + return callAfterLoad.call(this, plugins(system), system, { hasLoaded: calledSomething }) + + if(isArray(plugins)) { + return plugins.map(plugin => callAfterLoad.call(this, plugin, system, { hasLoaded: calledSomething })) + } + + return calledSomething +} + // Wraps deepExtend, to account for certain fields, being wrappers. // Ie: we need to convert some fields into arrays, and append to them. // Rather than overwrite diff --git a/test/core/system/system.js b/test/core/system/system.js index 205fc8a6..fc5db9a1 100644 --- a/test/core/system/system.js +++ b/test/core/system/system.js @@ -684,13 +684,13 @@ describe("bound system", function(){ }) describe("afterLoad", function() { - it("should call an plugin's `afterLoad` method after the plugin is loaded", function() { + it("should call a plugin's `afterLoad` method after the plugin is loaded", function() { // Given const system = new System({ plugins: [ { afterLoad(system) { - system.wow = system.dogeSelectors.wow + this.rootInjects.wow = system.dogeSelectors.wow }, statePlugins: { doge: { @@ -705,6 +705,89 @@ describe("bound system", function(){ ] }) + // When + var res = system.getSystem().wow() + expect(res).toEqual("so selective") + }) + it("should call a preset plugin's `afterLoad` method after the plugin is loaded", function() { + // Given + const MyPlugin = { + afterLoad(system) { + this.rootInjects.wow = system.dogeSelectors.wow + }, + statePlugins: { + doge: { + selectors: { + wow: () => (system) => { + return "so selective" + } + } + } + } + } + + const system = new System({ + plugins: [ + [MyPlugin] + ] + }) + + // When + var res = system.getSystem().wow() + expect(res).toEqual("so selective") + }) + it("should call a function preset plugin's `afterLoad` method after the plugin is loaded", function() { + // Given + const MyPlugin = { + afterLoad(system) { + this.rootInjects.wow = system.dogeSelectors.wow + }, + statePlugins: { + doge: { + selectors: { + wow: () => (system) => { + return "so selective" + } + } + } + } + } + + const system = new System({ + plugins: [ + () => { + return [MyPlugin] + } + ] + }) + + // When + var res = system.getSystem().wow() + expect(res).toEqual("so selective") + }) + it("should call a registered plugin's `afterLoad` method after the plugin is loaded", function() { + // Given + const MyPlugin = { + afterLoad(system) { + this.rootInjects.wow = system.dogeSelectors.wow + }, + statePlugins: { + doge: { + selectors: { + wow: () => (system) => { + return "so selective" + } + } + } + } + } + + const system = new System({ + plugins: [] + }) + + system.register([MyPlugin]) + // When var res = system.getSystem().wow() expect(res).toEqual("so selective")