Hold that thought on saving time and work with/without module.config.js. I realized, there is a lot more that could be saved.
Tutor is a amazing feat of engineering and one great thing about it is - it’s opinionated. So, it makes a lot of decisions and codifies it for us. But it also trying to meet different requirements for different people using patches. Which is great. But by doing so, it becomes a bit unopiniontated. That means it leaves it open for the plugin devs to decide which patches to use. The patches come with more decisions regarding “when” and “hows” - post/pre install, build/runtime mounts..etc., Again great for flexibility.
The trade off is, there is a whole lot of strings where there could be typed Python (think openedx-common-lms-settings), validated YAML (think lms-env, lms-env-features), validated JS (think mfe-* patches). Now we are getting to a place where a set of decisions are being made to achieve specific goals - the sample-plugin, but we are storing it in documentation. What if we stored those decisions in code?
Taking the frontend plugin as an example - 3 things are must:
mfe-dockerfile-post-npm-install to install the package
mfe-env-config-buildtime-imports to patch the env.config.jsx file with the imports
PLUGIN_SLOTS.add_items() block to patche the JS
Now each of these 3 use multiline strings for configuration. It’s a pre-made decision for the goal of adding a component in a slot. So, why make the devs write it out as if they got to make it? Why can’t we simply define it like:
FrontendPlugin(
package="frontend-plugin-my-plugin",
local_path="/path/to/plugin-code",
slots=[
Slot(
mfe="authoring",
slot_name="org.openedx.frontend.authoring.course_unit_sidebar.v2",
component="MyAwesomeComponent",
operations=INSERT,
priority=10
)
]
)
and it doesn’t matter if build-context needs to be patched, module.config.js gets created or resolve flag is changed or not. We don’t have to worry if the plugin slot definition is missing a comma or if priority is a string or an integer, don’t have to wonder if mfe-env-config-builtdtime-imports or mfe-env-config-runtime-imports needs to be patched …etc.,
With that idea, I experimented with an LLM tool to create a proof of concept library → GitHub - tecoholic/tutor-patches: Proof of concept library for writing typed, validated Tutor plugin patches.
With that, I can now simplify this Tutor plugin
from tutormfe.hooks import PLUGIN_SLOTS
from tutor import hooks
hooks.Filters.ENV_PATCHES.add_item(
(
"mfe-dockerfile-post-npm-install",
"""
# Install the LTI Provider frontend plugin package
RUN npm install @tecoholic/frontend-plugin-lti-provider
""",
)
)
hooks.Filters.ENV_PATCHES.add_item(
(
"mfe-env-config-buildtime-imports",
"""
import { AuthoringUnitPageSidebarWidget } from '@tecoholic/frontend-plugin-lti-provider';
""",
)
)
PLUGIN_SLOTS.add_items(
[
(
"authoring",
"org.openedx.frontend.authoring.course_unit_sidebar.v2",
"""
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
priority: 60,
id: 'lti-provider-widget',
type: DIRECT_PLUGIN,
RenderWidget: AuthoringUnitPageSidebarWidget
}
}""",
),
]
)
to a much simplified version
from tutor_patches import FrontendPlugin, Slot, Operation
FrontendPlugin(
package="@tecoholic/frontend-plugin-lti-provider",
slots=[
Slot(
mfe="authoring",
slot_name="org.openedx.frontend.authoring.course_unit_sidebar.v2",
component="AuthoringUnitPageSidebarWidget"
)
]
).register()
The library uses Pydantic Models for FrontendPlugin and Slot. So, it catches issues like wrong types, non-existent MFEs..etc.,
It works great in editors with “hover for documentation” support.
Why do this?
Improve DevEx. We are at a point where unavoidable decisions are being made to accomplish certain goals, but adherence to those are left to the developers. So a lot of time & work are being spent in documenting them in different places and searching for that documentation. So, putting them down into a library instead feels like a big win.
Disclaimer about the library
The tutor-patches linked in this comment is not in anyway intended to be a “project”. It’s built with an LLM to express an idea. Intended to be thrown away.