fix: pin Monaco to 0.52.2 to stop intermittent editor load failures

Merged
BO
3mo ago
+29-3

What does this PR aim to accomplish?:

Closes #991 — Monaco Editor intermittently fails to load on submitpage.php, leaving the plain <textarea> fallback. The console shows:

Uncaught TypeError: Property description must be an object: undefined
    at r (monaco.contribution.js:1:133)
    at new i (monaco.contribution.js:1:357)

This is upstream microsoft/monaco-editor#5015, a regression in 0.53.0 — the exact version MonacoCDN was pinned to (added in #989).

0.53.0 switched to a chunked build whose minified helper variables are declared with var at file top level. AMD chunks are plain scripts, not ES modules, so those helpers land on window. Probing a live 0.53.0 load confirms 13 leaked single-letter globals:

h=Object.defineProperty  m=function(3)  r=function(3)  o=function(3)  p=function(3)
s=function(3)  i=function(3)  f=function(3)  u=function(3)  w=Object.defineProperty
M=function(3)  y=Object.defineProperty  C=function(3)

m is the collision. In language/css/monaco.contribution.js it is the field-setter helper; in yaml.contribution.6f43d486.js it is Object.defineProperty. Whichever chunk script evaluates last wins, and that depends on network timing — hence the "only sometimes, reproduce by refreshing" behaviour.

When yaml wins, the css chunk's r(this, "_options") (two args, so the value is undefined) becomes Object.defineProperty(this, "_options", undefined). Replaying that in isolation reproduces the reported message verbatim:

var m = Object.defineProperty;                        // what yaml.contribution.js sets global `m` to
var r = (s,e,t) => (m(s, typeof e != "symbol" ? e+"" : e, t), t);  // css/monaco.contribution.js:1:133
r({}, "_options");
// TypeError: Property description must be an object: undefined

Monaco then never finishes loading, the require(['vs/editor/editor.main']) callback never fires, ensureMonaco() rejects on its 30s timeout, and the fallback textarea from the issue's screenshot is shown.

How does this PR accomplish the above?:

  1. Pin MonacoCDN to 0.52.2 (XMOJ.user.js:554). 0.52.2 ships as a single editor.main.js bundle, so there are no cross-chunk globals to collide — the same probe against 0.52.2 comes back completely empty. Upstream fixed the leak in 0.54.0, but cdnjs has no stable release newer than 0.53.0 (0.54.0+ exist only on npm/jsDelivr). Since every other dependency in this script is served from cdnjs, and jsDelivr has a poor reliability record in mainland China, downgrading was preferred over switching CDN hosts. A comment in the code records why.

  2. Make ensureMonaco() single-flight (XMOJ.user.js:758). This was a second, independent way for loading to fail: overlapping callers (submit page editor, merge view, freopen snippets) each appended their own loader.js, and the second loader resets the AMD registry while the first is still resolving modules. The load promise is now cached, and cleared on failure so later calls can still retry.

No API surface changes — the script only uses monaco.editor.create, monaco.editor.createDiffEditor, monaco.editor.createModel, monaco.KeyMod and monaco.KeyCode, all long-stable and present in 0.52.2.

Testing performed: loaded cdnjs 0.52.2 in headless Chrome via CDP and confirmed the editor renders (.view-line elements present), that every Monaco API this script uses works, and that no globals leak. node --check XMOJ.user.js passes.


By submitting this pull request, I confirm the following:

  1. I have read and understood the contributor's guide, as well as this entire template.
  2. I have commented on my proposed changes within the code.
  3. I have tested my changes.
  4. I am willing to help maintain this change if there are issues with it later.
  5. It is compatible with the GNU General Public License v3.0.
  6. I have squashed any insignificant commits.
  7. I have checked that another pull request for this purpose does not exist.
  8. I have considered and confirmed that this submission will be valuable to others.
  9. I accept that this submission may not be used, and the PR can be closed at the will of the maintainer.
  10. I give this submission freely and claim no ownership to its content.
  11. ⚠️ Not verified — this was tested against the Monaco CDN in headless Chrome, not on a live xmoj.tech page. Please confirm the submit page editor works in both the new UI and the old/classic UI before merging.

  • I have read the above and my PR is ready for review.

Summary by Sourcery

Pin the Monaco editor CDN version and make Monaco loading single-flight to prevent intermittent failures of the code editor on the submit page.

Bug Fixes:

  • Resolve intermittent Monaco editor load failures that caused the submit page to fall back to a plain textarea when certain chunks collided globally.

Enhancements:

  • Ensure Monaco is loaded via a shared promise so overlapping callers reuse the same load sequence instead of injecting multiple loader scripts.

Summary by cubic

Pins monaco-editor on cdnjs to 0.52.2 to stop intermittent editor load failures on the submit page and makes ensureMonaco() single-flight. Bumps script version to 3.5.4. Closes #991.

  • Bug Fixes
    • Pinned monaco-editor to 0.52.2 to avoid the 0.53.0 global-leak regression that breaks AMD chunk loads.
    • Cached a shared load promise in ensureMonaco() (cleared on failure) to prevent duplicate loader.js inserts that reset the AMD registry.