Description
Many Frontity APIs need priorities. We should figure out a way to deal with priorities that is consistent across all the different Frontity APIs and can be used by both package creators and users.
Examples
A good use case that we could use to figure out how different solutions would look like could be:
-
We have two packages:
packageA
andpackageB
. -
Main
packageA
is:export default { actions: { packageA: { action1: () => { // ... }, }, }, server: { packageA: { middlware1: () => { // Needs to run before middlware2. }, middlware2: () => { // Needs to run after middlware1. }, }, }, };
-
Main
packageB
is:export default { actions: { packageA: { action1: () => { // Needs to overwrite `actions.packageA.action1` }, }, }, server: { packageB: { middlware3: () => { // Needs to run in between middlware1 and middlware2. }, }, }, };
-
The
frontity.config.js
file ofpackageA
is:export const settings = (prevSettings) => { // Needs to run after all the settings modifications of other packages. }; export const webpack = (prevConfig) => { // Needs to run before all the Webpack modifications of other packages. };
-
The
frontity.config.js
file ofpackageB
is:export const settings = (prevSettings) => { // Needs to run before all the settings modifications of other packages. }; export const webpack = (prevConfig) => { // Needs to run after all the Webpack modifications of other packages. };
Functionalities
Have a single way or API to define the priorities of the different Frontity APIs.
Requirements
- They can be configured at a function level.
- They work consistently across all the different APIs.
- They work across packages.
- They can be easily configured by package creators.
- They can be easily modified by other packages if needed.
- They are not required. When not set, a default middle priority should be used internally.
- Ideally, they can be easily modified by final users if needed.
Dependencies
This feature doesn’t have any dependencies, but there are some dependent features that will use this:
-
The AMP package: It needs priorities because it needs to modify the settings via
frontity.config.js
before any other package runs. - Server Extensibility: It needs priorities to make sure that the server middlware functions run in the correct order across packages.
- Frontity Hooks: It needs priorities to make sure that the hooks run in the correct order across packages.
- Webpack Customization: It needs priorities to make sure that the Webpack modifications run in the correct order across packages.
There are other Frontity APIs that are using priorities right now:
- Source Handlers.
- Html2React Processors.
- Fills (Slot and Fill).
What we are using for those is a priority
property on their configuration objects. If this FD ends up with a different solution to handle priorities in Frontity, we should migrate those APIs as well. Maybe not immediately, but over time, so all the priorities are consistent among Frontity APIs.
Possible solution
These are some initial ideas.
Configuration objects
This is what we are already using for Source Handlers, Html2React Processors and Fills.
Instead of a function, the developer needs to create an object that has the function inside a func
/processor
/… property. The priority is added as an optional property:
export default {
libraries: {
html2react: {
processors: [
{
processor: () => {
// ...
},
priority: 5,
},
],
},
},
};
Some pros (non-exhaustive):
- They can be overwritten by other packages and at runtime.
Some cons (non-exhaustive):
-
They require an object, although that can be solved by making it optional.
export default { libraries: { html2react: { processors: [ () => { // Doesn't require an object with default priority. }, { processor: () => { // Requires an object to set a custom priority. }, priority: 5, }, ], }, }, };
-
The requirement of the object is not that nice with namespaces, although not terrible I guess:
export default { server: { myPackage: { myFirstMiddleware: () => { // Default priority middlware. }, mySecondMiddleware: { fn: () => { // Custom priority middlware. }, priority: 5, }, }, }, };
Functions
This solution would be something similar to the add_action
and add_filter
functions of WordPress, where the first argument is the function and the second is the priority.
Something like this:
import { server } from "frontity";
export default {
server: {
myPackage: {
myFirstMiddleware: server(() => {
// Default priority middlware.
}),
mySecondMiddleware: server(() => {
// Custom priority middlware.
}, 5),
},
},
};
Some cons (non-exhaustive):
-
It requires an otherwise unnecessary extra import, although it could be avoided for default priorities.
-
It will require a function per API (server, handler, processor, hook…), although maybe it could be centralized with a single API:
import { priority } from "frontity";
export default {
server: {
myPackage: {
myFirstMiddleware: () => {
// Default priority middlware.
},
mySecondMiddleware: priority(() => {
// Custom priority middlware.
}, 5),
},
},
};
- Priorities are obscured and cannot be changed by other packages or at runtime.
Priorities export
Another solution that comes to my mind would be to add a separate export for priorities:
export default {
server: {
myPackage: {
myFirstMiddleware: () => {
// Default priority middlware.
},
mySecondMiddleware: () => {
// Custom priority middlware.
},
},
},
priorities: {
server: {
myPackage: {
mySecondMiddleware: 5,
},
},
},
};
Some pros (non-exhaustive):
- They can be overwritten by other packages and at runtime.
- All the priorities are stored in the same place (not really sure if that has a real benefit).
Some cons (non-exhaustive):
-
I am not sure how this could be integrated with the priorities required for the
frontiy.config.js
file because the store is not initialized at that point. Maybe we could do it with an additionalpriorities
export in that file:export const settings = () => { // Modify settings... } export const webpack = () => { // Modify webpack... } export const priorities = { settings: 5, webpack: 1 }
Priorities namespace
Similar to the priorities export, but in state
.
export default {
server: {
myPackage: {
myFirstMiddleware: () => {
// Default priority middlware.
},
mySecondMiddleware: () => {
// Custom priority middlware.
},
},
},
state: {
priorities: {
server: {
myPackage: {
mySecondMiddleware: 5,
},
},
},
},
};
Some pros (non-exhaustive):
- It can be changed in
frontity.settings.js
, although we could do that - Priorities would be reactive, so packages could subscribe to priority changes if they have initialization functions.
Some cons (non-exhaustive):
- I am not sure how this could be integrated with the priorities required for the
frontiy.config.js
file because the store is not initialized at that point.