Skip to content

fix: pin Monaco to 0.52.2 to stop intermittent editor load failures - #997

Merged
boomzero merged 3 commits into
devfrom
fix/monaco-load-failure-991
Jul 27, 2026
Merged

fix: pin Monaco to 0.52.2 to stop intermittent editor load failures#997
boomzero merged 3 commits into
devfrom
fix/monaco-load-failure-991

Conversation

@boomzero

@boomzero boomzero commented Jul 27, 2026

Copy link
Copy Markdown
Member

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.

Written for commit f0a8826. Summary will update on new commits.

Review in cubic

Monaco 0.53.0 declares its minified helper variables with `var` at the top
level of every chunk file. AMD chunks are plain scripts rather than modules,
so those helpers land on `window`: a live load leaks h, m, r, o, p, s, i, f,
u, w, M, y and C. Different chunks give the same name different meanings --
`m` is the field-setter helper in language/css/monaco.contribution.js but is
Object.defineProperty in yaml.contribution.6f43d486.js -- so whichever chunk
script evaluates last wins.

When yaml wins, the css chunk's `r(this, "_options")` call resolves to
Object.defineProperty(this, "_options", undefined) and throws
"Property description must be an object: undefined". The editor.main callback
then never fires, ensureMonaco() times out after 30s, and submitpage.php falls
back to the bare textarea. Evaluation order depends on network timing, which
is why it only reproduces on some page loads.

This is upstream microsoft/monaco-editor#5015, fixed in 0.54.0. cdnjs has no
stable release newer than 0.53.0, and the rest of the script sources from
cdnjs, so pin back to 0.52.2 -- it ships as a single bundle and leaks no
globals at all.

Also make ensureMonaco() single-flight. Overlapping callers (submit 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. A failed load clears the cached promise so later calls can retry.

Closes #991

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

🧙 Sourcery has finished reviewing your pull request!


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@hendragon-bot hendragon-bot Bot added the user-script This issue or pull request is related to the main user script label Jul 27, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 27, 2026

Copy link
Copy Markdown

Deploying xmoj-script-dev-channel with  Cloudflare Pages  Cloudflare Pages

Latest commit: f0a8826
Status: ✅  Deploy successful!
Preview URL: https://0d8671fd.xmoj-script-dev-channel.pages.dev
Branch Preview URL: https://fix-monaco-load-failure-991.xmoj-script-dev-channel.pages.dev

View logs

@pull-request-size pull-request-size Bot added size/M and removed size/S labels Jul 27, 2026

@sourcery-ai sourcery-ai Bot 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.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@boomzero

Copy link
Copy Markdown
Member Author

Can confirm works

@boomzero
boomzero merged commit 0da6227 into dev Jul 27, 2026
11 checks passed
@boomzero
boomzero deleted the fix/monaco-load-failure-991 branch July 27, 2026 02:01
@boomzero boomzero mentioned this pull request Jul 27, 2026
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/M user-script This issue or pull request is related to the main user script

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant