Thinking more about this, we may indeed wish to load modules from within the Vue app itself, to give us more flexibility, but for one thing, I don't think it would be that major of a change to the module architecture. More importantly, just because we're not installing the modules as proper Vue plugins via Vue.use(), that doesn't mean we can't use the same interface. Basically, we're just providing an object with an install method, which takes the Vue object as its first argument, and an optional object as the second argument, where we're supplying the store and the router. So we can just provide our own implementation of Vue.use() within the script loader, if we want to fire it off after the app has been initialized.
The most important use case for this is when the user logs in successfully, whether this is for the first time, before any modules have been loaded, or if it's been a while since using the app and the refresh token's expired, in which case it's likely that there may have been substantial changes to the modules.
The feasibility of moving away from the Vue plugin architecture will hinge primarily on two parts of the code: first, the loadFieldModule function, that loads the actual script:
Critically, as far as I know, all the procedures in these two parts of the code can be called after the Vue app has been instantiated, possibly from within a Vuex action, or in the App.vue file from a life cycle hook (or from a Vuex action that gets called from App.vue—that might actually be best).
So far as createFieldModule is concerned, I think we can just discard the install method and the whole wrapper object, and just run the procedures contained within it directly. Then loadFieldModule will not have to call Vue.use() at all, once it's called createFieldModule(). The one tricky thing might be component registration, which requires the static method Vue.component(), but I think we can pass the Vue class to createFieldModule or just import it where it's defined. We'll need to be a little careful about circular dependencies, but I think that will be manageable.
One last, possibly separate issue is whether to have a way of removing modules once they've been added. This would entail removing their references in the Vuex store and localStorage, removing their script tag from the DOM, and making sure the service worker knows to clear them from the cache.
Working on https://github.com/farmOS/farmOS-client/issues/341#issuecomment-657622417 got me thinking again about when we check the server for what modules are available, and how we load them, particularly how we right now depend on the Vue plugin API (
Vue.use(myPlugin)
must be called before the app is instantiated). I addressed this initially in https://github.com/farmOS/farmOS-client/issues/309#issuecomment-589247191:The most important use case for this is when the user logs in successfully, whether this is for the first time, before any modules have been loaded, or if it's been a while since using the app and the refresh token's expired, in which case it's likely that there may have been substantial changes to the modules.
The feasibility of moving away from the Vue plugin architecture will hinge primarily on two parts of the code: first, the
loadFieldModule
function, that loads the actual script:https://github.com/farmOS/farmOS-client/blob/63dab4bbf970d8b89b01a83a13c20a73cc7f7139/src/core/app.js#L31-L46
and second, the
createFieldModule
function, which calls the script, registers components, add routes, and commits module config to the store:https://github.com/farmOS/farmOS-client/blob/63dab4bbf970d8b89b01a83a13c20a73cc7f7139/src/utils/createFieldModule.js#L42-L70
Critically, as far as I know, all the procedures in these two parts of the code can be called after the Vue app has been instantiated, possibly from within a Vuex action, or in the
App.vue
file from a life cycle hook (or from a Vuex action that gets called fromApp.vue
—that might actually be best).So far as
createFieldModule
is concerned, I think we can just discard theinstall
method and the whole wrapper object, and just run the procedures contained within it directly. ThenloadFieldModule
will not have to callVue.use()
at all, once it's calledcreateFieldModule()
. The one tricky thing might be component registration, which requires the static methodVue.component()
, but I think we can pass the Vue class tocreateFieldModule
or just import it where it's defined. We'll need to be a little careful about circular dependencies, but I think that will be manageable.One last, possibly separate issue is whether to have a way of removing modules once they've been added. This would entail removing their references in the Vuex store and localStorage, removing their script tag from the DOM, and making sure the service worker knows to clear them from the cache.