Skip to content

feat!: make external scripts configurable per-app#227

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

feat!: make external scripts configurable per-app#227
arbrandes merged 1 commit intoopenedx:mainfrom
arbrandes:external-scripts

Conversation

@arbrandes
Copy link
Copy Markdown
Contributor

@arbrandes arbrandes commented Apr 15, 2026

Description

External scripts were previously hardcoded in initialize() with GoogleAnalyticsLoader as the default. This made them non-configurable and forced every site to bundle Google Analytics whether it was used or not.

This PR makes external scripts a per-app concern via a new externalScripts field on the App interface, following the same accumulation pattern as routes and slots. Each app's scripts receive that app's merged config (commonAppConfig + per-app config) via getAppConfig().

GoogleAnalyticsLoader is removed from frontend-base entirely. Operators who want it can author their own ExternalScriptLoader class and attach it to one of their apps.

See ADR 0014 for full rationale.

Demo

A working demonstration wiring a local GoogleAnalyticsLoader into a site's configs lives at openedx/frontend-template-site#13.

LLM usage notice

Built with assistance from Claude.

Comment thread docs/decisions/0014-external-scripts-and-contrib-apps.rst Outdated
Copy link
Copy Markdown
Member

@MaxFrank13 MaxFrank13 left a comment

Choose a reason for hiding this comment

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

Seems clean! A long-awaited feature for sure. Just had one question

Copy link
Copy Markdown
Contributor

@brian-smith-tcril brian-smith-tcril left a comment

Choose a reason for hiding this comment

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

Overall I think this makes a lot of sense. I do think there are a couple cases we should add documentation for though:

  • Writing a little app to load a script directly in site config
    • Likely a common use case, something we can support in Tutor MFE
  • Writing a loader and adding it to specific apps
    • The example in this PR is "the loader lives in an app, the app is always in the site" - having a "I just want to add this script on learner-dash" example would be nice

Another thing worth noting is that it seems like there are 2 ways to make sure a loader is always loaded: making an app for it and adding it to commonAppConfig. It'd be good to document why the app strategy is preferred over the commonAppConfig strategy (my understanding is that it's because if you put it in commonAppConfig you need to be more vigilant about having "don't run twice" guards in the loader itself)

Comment thread types.ts
Comment thread jest.config.js Outdated
Comment thread docs/decisions/0014-external-scripts-and-contrib-apps.rst Outdated
Comment thread docs/decisions/0014-external-scripts-and-contrib-apps.rst Outdated
Comment thread runtime/scripts/index.ts
@arbrandes
Copy link
Copy Markdown
Contributor Author

We've decided to drop Google Analytics from here entirely. It's going to be a Tutor plugin instead.

@arbrandes arbrandes force-pushed the external-scripts branch 5 times, most recently from 2df91bb to 0b61eeb Compare April 18, 2026 21:51
@arbrandes
Copy link
Copy Markdown
Contributor Author

a couple cases we should add documentation for

I added a fuller (but still small) example to the ADR. I think it's good enough for now.

"I just want to add this script on learner-dash"

This was possible in MFE Land, but it doesn't translate here very well (or at all)... That is, unless we we wire up a complicated mechanism with an unloadScript lifecycle hook. While this would justify using a class for the loader, I don't think we should open this can of worms right now.

@arbrandes
Copy link
Copy Markdown
Contributor Author

This is what the tutor plugin is going to look like:

openedx/openedx-tutor-plugins#63

Copy link
Copy Markdown
Contributor

@brian-smith-tcril brian-smith-tcril left a comment

Choose a reason for hiding this comment

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

LGTM!

I left a couple comments but nothing that should block merging this.

Comment thread docs/decisions/0014-per-app-external-scripts.rst
Comment thread docs/decisions/0014-per-app-external-scripts.rst
External scripts were previously hardcoded in initialize() with
GoogleAnalyticsLoader as the default. This made them non-configurable
and forced every site to bundle Google Analytics whether it was used
or not.

External scripts are now a per-app concern via the new externalScripts
field on the App interface, following the same accumulation pattern as
routes and slots. Each app's scripts receive that app's merged config
(commonAppConfig + per-app config) via getAppConfig().

GoogleAnalyticsLoader is removed from frontend-base entirely. Operators
who want it can author their own ExternalScriptLoader class and attach
it to one of their apps.

See ADR 0014 for full rationale.

BREAKING CHANGE: GoogleAnalyticsLoader is no longer provided by
frontend-base. Operators must supply their own external script loaders.

Co-Authored-By: Claude <noreply@anthropic.com>
@arbrandes arbrandes merged commit a9bc524 into openedx:main Apr 21, 2026
5 checks passed
@arbrandes arbrandes deleted the external-scripts branch April 21, 2026 14:28
@openedx-semantic-release-bot
Copy link
Copy Markdown

🎉 This PR is included in version 1.0.0-alpha.28 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for loading external scripts

4 participants