Skip to content

fix: align editor globals with WP Admin - #750

Merged
dcalhoun merged 14 commits into
trunkfrom
fix/expose-missing-wp-globals
Oct 2, 2026
Merged

dcalhoun merged 14 commits into
trunkfrom
fix/expose-missing-wp-globals

Conversation

@dcalhoun

@dcalhoun dcalhoun commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

What?

Improve alignment with the JavaScript globals found in the WP Admin environment.

Why?

We expose these globals to mimic the WP Admin environment, allowing plugins to access these extracted modules rather than bundling them themselves. This does not address a known bug, but improves robustness of the existing system.

How?

  • Expose four missing modules
  • Remove modules WP Admin does not expose
  • Flag namespace globals __esModule and expose default exports where core does
  • Define the screen globals from admin-header.php before plugins load
  • Configure AJAX as early as possible for invocations during plugin loading

Testing Instructions

General automated and manual regression testing, this does not address an observable bug.

Accessibility Testing Instructions

N/A, no user-facing changes.

Screenshots or screencast

N/A, no user-facing changes.


AI-generated details

Aligns the editor's globals with WP Admin, so plugin scripts bundled with @wordpress/dependency-extraction-webpack-plugin get what they get there. Globals we deliberately don't support stay out: legacy APIs, cookie auth, outdated libraries, and anything tied to admin screens or sidebars.

  • Missing packages: Adds blockSerializationDefaultParser, reduxRoutine, sync and uploadMedia, which core registers as wp-* scripts. sync and upload-media hold state, so plugins need the editor's own instance, as with fix: expose @wordpress/theme as window.wp.theme #614.
  • __esModule flag: Core builds each namespace global with esbuild's __toCommonJS. Webpack resolves a plugin's default import to default only when that flag is set. A new toCommonJS helper wraps every namespace global, including wp.hooks and wp.i18n.
  • Default exports: Core exposes the default export for packages flagged wpScriptDefaultExport. serverSideRender now exposes the component and shortcode the Shortcode class. isShallowEqual becomes a namespace, so named imports resolve.
  • Globals core doesn't define: Drops icons and globalStylesEngine. Core registers no such scripts and plugin tooling bundles both; since build(deps-dev): Bump @wordpress/dependency-extraction-webpack-plugin from 6.43.0 to 6.56.0 #739 our own imports bundle them too. formatLibrary is undefined, as core's script exports nothing. Its import in wordpress-globals.js registers the formats; the one in visual-editor never ran, because our Vite transform strips it, so it's removed.
  • Screen globals: admin-header.php sets ajaxurl, pagenow, typenow, adminpage, thousandsSeparator, decimalPoint and isRtl before any script runs. They are now defined before plugins load; previously ajaxurl arrived after them. The separators come from Intl.NumberFormat for now.
  • ReactJSXRuntime: Drops the interop default key core's global lacks.

Verified: make lint-web-fix, make test-web-unit, make check-wp-packages, and CI=true make test-web-e2e (50 passed) against wp-env with Jetpack. A capture of every global in WP Admin's post-new.php and in the built editor differs only in deliberately skipped globals. The screen globals match WP Admin's values, except that ajaxurl is absolute because the editor isn't served from the site's origin.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Mp7HPYAD9KtkU4Dh994Pfm

@github-actions github-actions Bot added the [Type] Bug An existing feature does not function as intended label Sep 30, 2026
@wpmobilebot

wpmobilebot commented Sep 30, 2026 •

Copy link
Copy Markdown

XCFramework Build

This PR's XCFramework is available for testing. Add the following to your Package.swift:

.package(url: "https://github.com/wordpress-mobile/GutenbergKit", branch: "pr-build/750")

Built from 61c08dc

@dcalhoun dcalhoun changed the title fix: expose four core script packages on window.wp fix: align window.wp globals with WP Admin Sep 30, 2026
@dcalhoun
dcalhoun force-pushed the fix/expose-missing-wp-globals branch from 3c34bf8 to fa41fd1 Compare October 1, 2026 12:07
@dcalhoun dcalhoun changed the title fix: align window.wp globals with WP Admin fix: align editor globals with WP Admin Oct 1, 2026
@dcalhoun
dcalhoun force-pushed the fix/expose-missing-wp-globals branch from 64ee862 to 9c624ec Compare October 1, 2026 12:29
@dcalhoun
dcalhoun marked this pull request as ready for review October 1, 2026 14:28
@dcalhoun
dcalhoun requested a review from adalpari October 1, 2026 14:28
dcalhoun and others added 11 commits October 1, 2026 13:04
They were only reachable transitively, but the editor will import them to
expose them on window.wp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mp7HPYAD9KtkU4Dh994Pfm
Core builds each namespace global with esbuild's __toCommonJS, which plugin
bundlers read to resolve a default import to `default`. Without the flag,
a plugin's default import received the whole namespace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mp7HPYAD9KtkU4Dh994Pfm
WordPress registers wp-block-serialization-default-parser, wp-redux-routine,
wp-sync and wp-upload-media, so plugins may import them as window.wp
externals. They were undefined in the editor, and the store-holding
packages must be the editor's own instance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mp7HPYAD9KtkU4Dh994Pfm
Core exposes the default export for packages flagged wpScriptDefaultExport,
which includes server-side-render and shortcode but not is-shallow-equal.
A plugin's ServerSideRender default import received the namespace, and
named is-shallow-equal imports were undefined.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mp7HPYAD9KtkU4Dh994Pfm
Core registers no icons or global-styles-engine scripts, and plugin
tooling bundles both, so nothing reads them; since #739 our own imports
bundle them too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mp7HPYAD9KtkU4Dh994Pfm
Core's format-library script exports nothing, so its global is
undefined. The side-effect import stays, as it registers the formats.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mp7HPYAD9KtkU4Dh994Pfm
The wordPressExternals transform strips side-effect @WordPress imports
outside wordpress-globals.js, so this import never ran; the one in
wordpress-globals.js registers the formats.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mp7HPYAD9KtkU4Dh994Pfm
admin-header.php sets ajaxurl, pagenow, typenow, adminpage, the number
separators and isRtl before any script runs. Plugins read them while
loading, but GBK lacked most and set ajaxurl only after plugins loaded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mp7HPYAD9KtkU4Dh994Pfm
Core exposes react/jsx-runtime's CommonJS exports; the namespace import
added a `default` key that core's global lacks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mp7HPYAD9KtkU4Dh994Pfm
configureLocale() returns the direction so callers don't derive it a
second time; isRtl now uses it instead of re-checking the locale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ajaxurl is now defined while plugins load, so their load-time requests
reached admin-ajax.php without the Authorization header. The media
aliases stay after plugin loading so a plugin replacing wp.media can't
discard them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dcalhoun
dcalhoun force-pushed the fix/expose-missing-wp-globals branch from 5c68b59 to f02272e Compare October 1, 2026 17:07
@adalpari

adalpari commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Manual testing looks good to me!

Claude found these "medium" points, but I'm not sure they are at that severity level. So, just for you to double check,

# Severity File Finding Why it matters Suggested fix
1 🟡 Medium ajax.js:78 The auth prefilter decides isSameOrigin( options.url ) in its body but attaches the header in beforeSend; moving configureAjax() ahead of plugin load registers it first, so plugin prefilters now run in between. A plugin prefilter assigning options.beforeSend discards our wrapper and admin-ajax goes out unauthenticated; one rewriting options.url cross-origin gets the token sent there — not CORS-gated on iOS, which loads from file:// with allowUniversalAccessFromFileURLs. Both reproduced against the installed jQuery with a clean trunk-vs-branch differential. Re-evaluate isSameOrigin( options.url, siteOrigin ) inside the beforeSend wrapper rather than in the prefilter body.
2 🟡 Medium admin-globals.js:37 getNumberSeparators() derives separators from Intl, but core prints $wp_locale->number_format from the site's translations — and the two disagree. fr gets U+202F vs core's U+00A0, ru U+00A0 vs plain U+0020, ar U+066B vs . — 16 of 49 shipped locales. ICU 72 changed fr, so one build yields different values across WebView versions. A plugin splitting a number_format_i18n() string on window.thousandsSeparator parses 1 234 567,80 as 1. Source the separators from the translation bundles or an explicit map. Also warn() in the catch at line 43 — it never fires for well-formed-but-unsupported tags like kab, which silently resolve to the device locale rather than the promised en_US.

@adalpari adalpari 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.

🚢 it!

dcalhoun and others added 3 commits October 2, 2026 08:34
This reverts commit f02272e.

Registering the auth prefilter first let plugin prefilters run after it,
replacing beforeSend or rewriting the URL after the origin check. Trunk's
order runs it after load-time prefilters; load-time AJAX is rare, and
admin-ajax ignores the header unless a handler opts in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mp7HPYAD9KtkU4Dh994Pfm
Core derives thousandsSeparator and decimalPoint from its own translations,
which Intl doesn't reproduce: values differ for many locales and across
WebView ICU versions. They serve admin list screens the editor lacks, and
plugins rarely read them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mp7HPYAD9KtkU4Dh994Pfm
The prefilter checked the origin before later prefilters ran, so one
rewriting the URL off the site would still send the token there. The
wrapper also called the original beforeSend without its settings, its
context, or its return value, so a `false` return didn't cancel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mp7HPYAD9KtkU4Dh994Pfm
@dcalhoun

dcalhoun commented Oct 2, 2026

Copy link
Copy Markdown
Member Author

Thank you for testing and reviewing, @adalpari.

AJAX auth prefilter order: Valid and addressed. I reverted the re-ordering in 9e49b1e, as its original value is very low and unlikely to occur (plugin AJAX requests requiring auth triggered during load; Jetpack is the only opt-in plugin at the moment). I added the origin recheck hardening in 61c08dc.

Number separators: I opted to remove these globals in 27ceb63 after determining they are largely used for WP Admin list pages. They do not appear to be worth their weight, particularly given the gaps you noted.

@dcalhoun
dcalhoun enabled auto-merge (squash) October 2, 2026 13:06
@dcalhoun
dcalhoun merged commit 2d6a980 into trunk Oct 2, 2026
24 checks passed
@dcalhoun
dcalhoun deleted the fix/expose-missing-wp-globals branch October 2, 2026 13:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Type] Bug An existing feature does not function as intended

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants