looking at discourse/discourse explain the new patterns for js dependency distribution, investigate how mathjax is distributed and how fullcalendar is. Reply with a quick doc of “old way”, “new way” and how to port mathjax to new pattern
looking at discourse/discourse explain the new patterns for js dependency distribution, investigate how mathjax is distributed and how fullcalendar is. Reply with a quick doc of “old way”, “new way” and how to port mathjax to new pattern
GitHub search files
Searched for ‘mathjax’ in discourse/discourse/main
GitHub search files
Searched for ‘fullcalendar’ in discourse/discourse/main
GitHub code search
Searched for ‘fullcalendar’ in discourse/discourse
GitHub file content
Retrieved content of frontend/discourse/app/static/full-calendar-bundle.js, frontend/discourse/app/lib/load-full-calendar.js, frontend/discourse/package.json, plugins/discourse-math/assets/javascripts/initializers/discourse-math-mathjax.js from discourse/discourse@main
Now let me look at how fullcalendar is actually consumed in the calendar plugin:
GitHub file content
Retrieved content of plugins/discourse-calendar/assets/javascripts/discourse/components/full-calendar.gjs from discourse/discourse@main
Now let me search for more context on the static bundle patterns and how the old way worked:
GitHub search files
Searched for ‘static, bundle’ in discourse/discourse/main
GitHub code search
Searched for ‘loadScript public’ in discourse/discourse
GitHub file content
Retrieved content of frontend/discourse/app/static/ace-editor-bundle.js, frontend/discourse/app/lib/load-script.js from discourse/discourse@main
GitHub code search
Searched for ‘ace-editor-bundle’ in discourse/discourse
Discourse has moved from vendoring/public folder distribution to proper npm dependencies with static bundles for large external libraries. Here’s how FullCalendar (new pattern) compares to MathJax (old pattern).
MathJax is distributed by vendoring the entire library into the plugin’s public/ folder:
Structure:
plugins/discourse-math/
├── public/
│ └── mathjax/
│ ├── MathJax.2.7.5.js
│ └── extensions/
│ └── ... (dozens of files)
Loading mechanism (discourse-math-mathjax.js):
import loadScript from "discourse/lib/load-script";
function ensureMathJax(opts) {
initMathJax(opts);
return loadScript("/plugins/discourse-math/mathjax/MathJax.2.7.5.js");
}
Problems:
window.MathJax instead of ES modulesloadScript() injects a <script> tag at runtimeFullCalendar uses npm packages with a static bundle pattern:
frontend/discourse/package.json:
{
"dependencies": {
"@fullcalendar/core": "^6.1.19",
"@fullcalendar/daygrid": "^6.1.19",
"@fullcalendar/list": "^6.1.19",
"@fullcalendar/moment": "^6.1.19",
"@fullcalendar/moment-timezone": "^6.1.19",
"@fullcalendar/timegrid": "^6.1.19"
}
}
frontend/discourse/app/static/full-calendar-bundle.js:
export { Calendar } from "@fullcalendar/core";
export { default as DayGrid } from "@fullcalendar/daygrid";
export { default as TimeGrid } from "@fullcalendar/timegrid";
export { default as List } from "@fullcalendar/list";
export { default as MomentPlugin } from "@fullcalendar/moment";
export { default as MomentTimezonePlugin } from "@fullcalendar/moment-timezone";
frontend/discourse/app/lib/load-full-calendar.js:
import { waitForPromise } from "@ember/test-waiters";
export default async function loadFullCalendar() {
const promise = import("discourse/static/full-calendar-bundle");
waitForPromise(promise);
return await promise;
}
import loadFullCalendar from "discourse/lib/load-full-calendar";
export default class FullCalendar extends Component {
@action
async setupCalendar(element) {
const calendarModule = await loadFullCalendar();
this.calendar = new calendarModule.Calendar(element, {
plugins: [
calendarModule.DayGrid,
calendarModule.TimeGrid,
// ...
],
// ...
});
}
}
Benefits:
import()waitForPromise integration for testsAdd MathJax 3.x to frontend/discourse/package.json:
{
"dependencies": {
"mathjax": "^3.2.2"
}
}
Create frontend/discourse/app/static/mathjax-bundle.js:
// MathJax 3 has a different API - example for TeX to SVG
import { mathjax } from "mathjax-full/js/mathjax";
import { TeX } from "mathjax-full/js/input/tex";
import { SVG } from "mathjax-full/js/output/svg";
import { browserAdaptor } from "mathjax-full/js/adaptors/browserAdaptor";
import { RegisterHTMLHandler } from "mathjax-full/js/handlers/html";
export { mathjax, TeX, SVG, browserAdaptor, RegisterHTMLHandler };
Or for simpler use with mathjax-full’s built-in component:
export * from "mathjax-full/js/mathjax";
export { TeX } from "mathjax-full/js/input/tex/tex";
export { CHTML } from "mathjax-full/js/output/chtml/chtml";
// ... export what you need
Create frontend/discourse/app/lib/load-mathjax.js:
import { waitForPromise } from "@ember/test-waiters";
export default async function loadMathJax() {
const promise = import("discourse/static/mathjax-bundle");
waitForPromise(promise);
return await promise;
}
Update plugins/discourse-math/assets/javascripts/initializers/discourse-math-mathjax.js:
import loadMathJax from "discourse/lib/load-mathjax";
let mathjaxInstance = null;
async function ensureMathJax(opts) {
if (mathjaxInstance) {
return mathjaxInstance;
}
const { mathjax, TeX, CHTML, browserAdaptor, RegisterHTMLHandler } =
await loadMathJax();
const adaptor = browserAdaptor();
RegisterHTMLHandler(adaptor);
mathjaxInstance = mathjax.document(document, {
InputJax: new TeX({ /* options */ }),
OutputJax: new CHTML({ /* options */ })
});
return mathjaxInstance;
}
// Update decorate() to use MathJax 3 API
async function decorate(elem, isPreview, opts) {
const mjDoc = await ensureMathJax(opts);
// Use mjDoc.render() or similar MathJax 3 methods
}
Remove the entire plugins/discourse-math/public/mathjax/ directory.
| Aspect | Old (MathJax) | New (FullCalendar) |
|---|---|---|
| Package source | Vendored in public/ |
npm in package.json |
| Version | 2.7.5 (manual) | ^6.1.19 (semver) |
| Loading | loadScript() + <script> tag |
import() dynamic ES module |
| Exports | Global window.MathJax |
ES module exports |
| Bundle location | /plugins/.../mathjax/ |
app/static/*-bundle.js |
| Updates | Manual copy | Dependabot PRs |
| Tree-shaking | None | Webpack optimized |