Skip to content

feat: add EXTERNAL_SCRIPTS hook for configuring MFE external scripts#290

Merged
arbrandes merged 1 commit into
overhangio:mainfrom
arbrandes:external-scripts
Apr 21, 2026
Merged

feat: add EXTERNAL_SCRIPTS hook for configuring MFE external scripts#290
arbrandes merged 1 commit into
overhangio:mainfrom
arbrandes:external-scripts

Conversation

@arbrandes

@arbrandes arbrandes commented Apr 15, 2026

Copy link
Copy Markdown
Collaborator

Description

frontend-platform recently made externalScripts configurable via env.config.js, following the same pattern used for loggingService, analyticsService, and authService. This allows script loaders (such as GoogleAnalyticsLoader) to be declared in configuration rather than hardcoded in initialize().

This PR adds an EXTERNAL_SCRIPTS hook to tutor-mfe so that Tutor plugins can register external scripts without resorting to patches. The hook follows the same pattern as PLUGIN_SLOTS: each item is a (mfe_name, script_config) tuple, where mfe_name is "all" or a specific MFE name, and script_config is the JavaScript expression to add to the externalScripts config array.

In the env.config.jsx template, external scripts are wired up outside the frontend-plugin-framework try/catch block, since they don't depend on FPF. The externalScripts key is only set when scripts are actually configured, so MFE-level defaults are preserved when no scripts are registered.

Depends on openedx/frontend-platform#876.

Testing

Baseline: no scripts registered. With a stock mfe plugin, run tutor config save and inspect the rendered env/build/mfe/apps/<app>/env.config.jsx. Verify that no addExternalScripts(...) calls are emitted and that config.externalScripts is never assigned, so any MFE-level defaults remain untouched.

All-MFE registration. Create a throwaway Tutor plugin at $(tutor plugins printroot)/fakeloader.py with the following contents:

from tutor import hooks
from tutormfe.hooks import EXTERNAL_SCRIPTS

hooks.Filters.ENV_PATCHES.add_item(
    (
        "mfe-env-config-buildtime-definitions",
        """
class FakeLoaderAll {
  constructor() {}
  loadScript() {
    console.log('FakeLoaderAll ran');
  }
}
""",
    )
)

EXTERNAL_SCRIPTS.add_items([
    ("all", "FakeLoaderAll"),
])

Enable it with tutor plugins enable fakeloader, then run tutor config save and inspect env/build/mfe/apps/<app>/env.config.jsx for any MFE. Confirm FakeLoaderAll is defined in the buildtime section and that addExternalScripts(config, [FakeLoaderAll]); appears outside any APP_ID guard. Run tutor dev launch (or your usual dev flow), load an MFE that is running the latest version of frontend-platform (check openedx/public-engineering#505) in a browser, and confirm FakeLoaderAll ran is logged to the console on both.

Per-MFE registration. Extend the same plugin file with a second loader targeted at a single MFE:

hooks.Filters.ENV_PATCHES.add_item(
    (
        "mfe-env-config-buildtime-definitions",
        """
class FakeLoaderLearning {
  constructor() {}
  loadScript() {
    console.log('FakeLoaderLearning ran');
  }
}
""",
    )
)

EXTERNAL_SCRIPTS.add_items([
    ("learning", "FakeLoaderLearning"),
])

Re-run tutor config save and confirm the learning MFE's env.config.jsx wraps the FakeLoaderLearning call in if (process.env.APP_ID == 'learning'), while other MFEs do not get that call at all. Reload the learning MFE and verify both FakeLoaderAll ran and FakeLoaderLearning ran appear in the console; reload authn and verify only FakeLoaderAll ran appears.

Plugin disabled. Run tutor plugins disable fakeloader && tutor config save and confirm both the loader class definitions and the addExternalScripts calls disappear from the rendered templates, and that config.externalScripts is no longer assigned.

LLM usage notice

Built with assistance from Claude.

@mboisson

mboisson commented Apr 17, 2026

Copy link
Copy Markdown

One thing that is not clear to me in this one is whether the default GA loader is kept if we do this:

    EXTERNAL_SCRIPTS.add_items([
        (
            "all",
            "CookieYesLoader",
        ),
    ])

and if it is, how would one remove it if they want to completely replace the external scripts.

@arbrandes

arbrandes commented Apr 17, 2026

Copy link
Copy Markdown
Collaborator Author

@mboisson

whether the default GA loader is kept

It's not kept. If you add any loaders here, the defaults are replaced. (The flip side is that if you still want it, you have to add it explicitly.)

@mboisson

mboisson commented Apr 17, 2026

Copy link
Copy Markdown

@mboisson

whether the default GA loader is kept

It's not kept. If you add any loaders here, the defaults are replaced. (The flip side is that if you still want it, you have to add it explicitly.)

Ok, so it's not really an "add_items" (i.e. "add_items" kind of means that this only adds new scripts, not remove default ones).

And if you call add_items multiple times, it adds each script without overwriting the previous ones ?

@arbrandes

arbrandes commented Apr 18, 2026

Copy link
Copy Markdown
Collaborator Author

if you call add_items multiple times, it adds each script without overwriting the previous ones ?

Correct. The replacement is just about the frontend-platform default, Google Analytics, which will soon be deprecated (by frontend-platform), anyway. We expect it to become a standalone Tutor plugin that takes advantage of the mechanism introduced here.

@Faraz32123 Faraz32123 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Co-Authored-By: Claude <noreply@anthropic.com>
@arbrandes
arbrandes merged commit 835406b into overhangio:main Apr 21, 2026
2 checks passed
@github-project-automation github-project-automation Bot moved this from Pending Triage to Done in Tutor project management Apr 21, 2026
@arbrandes
arbrandes deleted the external-scripts branch April 21, 2026 10:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

4 participants