Skip to content

Commit 4d733c3

Browse files
authored
refactor: Organize editor setup (#242)
* refactor: Relocate jQuery global assignment to environment setup Relates to environment setup. * docs: Update outdated CLAUDE.md Make target reference * refactor: Editor setup prefers async/await over Promises Improve readability. * refactor: Rename WordPress globals function * refactor: Update locale comment Use consistent terms between functions and comments. * refactor: Extract body class utility Simplify `setUpEditorEnvironment` logic to improve readability. * refactor: Organize imports to follow Gutenberg project practices * refactor: Expose jQuery alongside other WordPress globals * refactor: Avoid import side effects * refactor: Update imports to follow Gutenberg project practices * refactor: Document editor environment functions * refactor: Remove unnecessary localization lazy loading This modules does not reference `window.wp` globals other than those loaded by the `wordpress-i18n` module via the script tag in the entry `index.html` file. * refactor: Wrap all editor set up logic within try-catch * refactor: Reduce repetition in `setBodyClasses` utility * refactor: Extract platform detection logic Reduce duplication. * refactor: Remove unused return value This utility is solely used to await the availability of the bridge configuration. The value itself is retrieved via `getGBKit()`. * refactor: Rename functions for consistency * refactor: Node.js ESM module import include file ending Node.js ESM stictly follows the ECMAScript spec, which requires module file endings. Vite, however, is more forgiving. We could consider updating all project imports for consistency. * refactor: Relocate error load notice to directory Align with all other component organization. * refactor: Align native inserter name with core inserter * refactor: Correct editor load notice description * refactor: Remove unused export * refactor: Simplify body class utility * refactor: Reuse default locale constant
1 parent 75c9788 commit 4d733c3

File tree

16 files changed

+208
-122
lines changed

16 files changed

+208
-122
lines changed

CLAUDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ make lint-fix-js
6767
npm run lint:js:fix
6868

6969
# Format JavaScript code
70-
make format-js
70+
make format
7171
# or
7272
npm run format
7373
```
@@ -193,5 +193,5 @@ Always run these commands before committing:
193193
make lint-js
194194

195195
# Format JavaScript code
196-
make format-js
196+
make format
197197
```

src/components/editor-load-notice.jsx renamed to src/components/editor-load-notice/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const pluginLoadFailedNotice = __(
1111
);
1212

1313
/**
14-
* Displays a notice with actions to retry or dismiss.
14+
* Displays a notice regarding the plugin loading outcome.
1515
*
1616
* @param {Object} props Component props.
1717
* @param {string} props.className Additional class names to apply.

src/components/editor-load-notice.test.jsx renamed to src/components/editor-load-notice/index.test.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { render, screen, fireEvent, act } from '@testing-library/react';
77
/**
88
* Internal dependencies
99
*/
10-
import EditorLoadNotice from './editor-load-notice';
10+
import EditorLoadNotice from '.';
1111

1212
vi.mock( '@wordpress/i18n' );
1313
vi.mock( '@wordpress/components' );

src/components/editor-toolbar/index.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import './style.scss';
2828
import { useModalize } from './use-modalize';
2929
import { useModalDialogState } from '../editor/use-modal-dialog-state';
3030
import { getGBKit } from '../../utils/bridge';
31-
import NativeBlockInserterButton from '../native-block-inserter-button';
31+
import NativeInserter from '../native-inserter';
3232
import { useScrollIndicators } from './use-scroll-indicators';
3333

3434
/**
@@ -98,7 +98,7 @@ const EditorToolbar = ( { className } ) => {
9898
);
9999

100100
const addBlockButton = enableNativeBlockInserter ? (
101-
<NativeBlockInserterButton
101+
<NativeInserter
102102
open={ isInserterOpened }
103103
onToggle={ setIsInserterOpened }
104104
/>

src/index.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
1-
/**
2-
* External dependencies
3-
*/
4-
import jquery from 'jquery';
5-
61
/**
72
* Internal dependencies
83
*/
94
import { setUpEditorEnvironment } from './utils/editor-environment';
105
import './index.scss';
116

12-
window.jQuery = jquery; // Expose jQuery for plugins
137
setUpEditorEnvironment();

src/utils/api-fetch.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { getGBKit } from './bridge';
1818
*
1919
* @return {void}
2020
*/
21-
export function initializeApiFetch() {
21+
export function configureApiFetch() {
2222
const { siteApiRoot = '' } = getGBKit();
2323

2424
apiFetch.use( apiFetch.createRootURLMiddleware( siteApiRoot ) );

src/utils/api-fetch.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import apiFetch from '@wordpress/api-fetch';
1919
/**
2020
* Internal dependencies
2121
*/
22-
import { initializeApiFetch } from './api-fetch';
22+
import { configureApiFetch } from './api-fetch';
2323
import * as bridge from './bridge';
2424

2525
vi.mock( './bridge' );
@@ -34,7 +34,7 @@ describe( 'api-fetch credentials handling', () => {
3434
} );
3535

3636
// Initialize middleware once - it will persist across all tests
37-
initializeApiFetch();
37+
configureApiFetch();
3838
} );
3939

4040
beforeEach( () => {

src/utils/bridge.js

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -288,33 +288,23 @@ export function logException(
288288
*
289289
* @param {number} timeoutMs Timeout in milliseconds after which to reject.
290290
*
291-
* @return {Promise<GBKitConfig>} Promise that resolves with GBKit config or rejects after timeout.
291+
* @return {Promise<void>} Promise that resolves with GBKit config or rejects after timeout.
292292
*/
293293
export function awaitGBKitGlobal( timeoutMs = 3000 ) {
294294
return new Promise( ( resolve, reject ) => {
295295
const startTime = Date.now();
296296

297297
const checkGBKit = () => {
298298
if ( window.GBKit ) {
299-
resolve( window.GBKit );
299+
resolve();
300300
return;
301301
}
302302

303303
if ( Date.now() - startTime >= timeoutMs ) {
304-
// In development mode, bypass the GBKit requirement and seed a default post,
305-
// allowing the editor to load without the native bridge to simplify testing.
304+
// In development mode, bypass the GBKit requirement allowing the editor
305+
// to load without the native bridge to simplify testing.
306306
if ( isDevMode() ) {
307-
resolve( {
308-
post: {
309-
id: -1,
310-
type: 'post',
311-
title: '',
312-
content: '',
313-
status: 'auto-draft',
314-
},
315-
themeStyles: false,
316-
hideTitle: false,
317-
} );
307+
resolve();
318308
return;
319309
}
320310

src/utils/editor-environment.js

Lines changed: 91 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
* Internal dependencies
33
*/
44
import { awaitGBKitGlobal, editorLoaded, getGBKit } from './bridge';
5+
import { configureLocale } from './localization';
56
import { loadEditorAssets } from './editor-loader';
67
import { initializeVideoPressAjaxBridge } from './videopress-bridge';
78
import EditorLoadError from '../components/editor-load-error';
89
import { error } from './logger';
910
import { setUpGlobalErrorHandlers } from './global-error-handler';
11+
import { Platform } from './platform';
1012
import './editor-styles';
1113

1214
/**
@@ -15,82 +17,115 @@ import './editor-styles';
1517
*
1618
* @return {Promise} Promise that resolves when initialization is complete
1719
*/
18-
export function setUpEditorEnvironment() {
19-
setUpGlobalErrorHandlers();
20+
export async function setUpEditorEnvironment() {
21+
try {
22+
setUpGlobalErrorHandlers();
23+
setBodyClasses();
24+
await awaitGBKitGlobal();
25+
await configureLocale();
26+
await initializeWordPressGlobals();
27+
await configureApiFetch();
28+
initializeVideoPressAjaxBridge();
29+
const pluginLoadResult = await loadPluginsIfEnabled();
30+
await initializeEditor( pluginLoadResult );
31+
} catch ( err ) {
32+
handleError( err );
33+
}
34+
}
2035

21-
// Detect platform and add class to body for platform-specific styling
22-
if ( typeof window !== 'undefined' && window.webkit ) {
23-
document.body.classList.add( 'is-ios' );
24-
} else if ( typeof window !== 'undefined' && window.editorDelegate ) {
25-
document.body.classList.add( 'is-android' );
36+
/**
37+
* Adds conditional CSS classes to `document.body`.
38+
*
39+
* @return {void}
40+
*/
41+
function setBodyClasses() {
42+
if ( typeof window === 'undefined' ) {
43+
return;
2644
}
2745

28-
// Rely upon promises rather than async/await to avoid timeouts caused by
29-
// circular dependencies. Addressing the circular dependencies is quite
30-
// challenging due to Vite's preload helpers and bugs in `manualChunks`
31-
// configuration.
32-
//
33-
// See:
34-
// - https://github.com/vitejs/vite/issues/18551
35-
// - https://github.com/vitejs/vite/issues/13952
36-
// - https://github.com/vitejs/vite/issues/5189#issuecomment-2175410148
37-
return awaitGBKitGlobal()
38-
.then( configureLocale )
39-
.then( loadRemainingGlobals )
40-
.then( initializeApiFetchWrapper )
41-
.then( initializeVideoPressAjaxBridge )
42-
.then( loadPluginsIfEnabled )
43-
.then( initializeEditor )
44-
.catch( handleError );
45-
}
46+
const classNames = [];
4647

47-
function configureLocale() {
48-
return import( './localization' ).then(
49-
( { configureLocale: _configureLocale } ) => _configureLocale()
50-
);
48+
if ( Platform.isIOS ) {
49+
classNames.push( 'is-ios' );
50+
} else if ( Platform.isAndroid ) {
51+
classNames.push( 'is-android' );
52+
}
53+
54+
window.document.body.classList.add( ...classNames );
5155
}
5256

53-
function loadRemainingGlobals() {
54-
// Load remaining WordPress globals after i18n is configured.
55-
return import( './wordpress-globals' );
57+
/**
58+
* Initialize WordPress global modules. Lazy-loaded to ensure the locale is
59+
* configured before importing these modules and referencing the corresponding
60+
* `window.wp` globals.
61+
*
62+
* @return {Promise} Promise that resolves when WordPress globals are initialized
63+
*/
64+
async function initializeWordPressGlobals() {
65+
const { initializeWordPressGlobals: _initializeWordPressGlobals } =
66+
await import( './wordpress-globals' );
67+
_initializeWordPressGlobals();
5668
}
5769

58-
function initializeApiFetchWrapper() {
59-
// Load api-fetch now that WordPress globals are available.
60-
return import( './api-fetch' ).then(
61-
( { initializeApiFetch: _initializeApiFetch } ) => {
62-
_initializeApiFetch();
63-
}
70+
/**
71+
* Configure `api-fetch` middleware and settings. Lazy-loaded to ensure
72+
* WordPress globals are available before importing `api-fetch` and
73+
* referencing `window.wp.apiFetch`.
74+
*/
75+
async function configureApiFetch() {
76+
const { configureApiFetch: _configureApiFetch } = await import(
77+
'./api-fetch'
6478
);
79+
_configureApiFetch();
6580
}
6681

67-
function loadPluginsIfEnabled() {
82+
/**
83+
* @typedef {Object} PluginLoadResult
84+
*
85+
* @property {string[] | undefined} [allowedBlockTypes] Array of allowed block types provided by the API.
86+
* @property {boolean} [pluginLoadFailed] Indicates if plugin loading failed.
87+
*/
88+
89+
/**
90+
* Load plugins if enabled in GBKit settings.
91+
*
92+
* @return {Promise<PluginLoadResult>} Promise resolving to plugin load results
93+
*/
94+
async function loadPluginsIfEnabled() {
6895
const { plugins } = getGBKit();
6996

7097
if ( plugins ) {
71-
return loadEditorAssets()
72-
.then( ( { allowedBlockTypes } ) => {
73-
return { allowedBlockTypes };
74-
} )
75-
.catch( () => {
76-
return Promise.resolve( { pluginLoadFailed: true } );
77-
} );
98+
try {
99+
const { allowedBlockTypes } = await loadEditorAssets();
100+
return { allowedBlockTypes, pluginLoadFailed: false };
101+
} catch {
102+
return { pluginLoadFailed: true };
103+
}
78104
}
79105

80-
return Promise.resolve( {} );
106+
return { pluginLoadFailed: false };
81107
}
82108

83-
function initializeEditor( pluginLoadResult = {} ) {
84-
return import( './editor' ).then(
85-
( { initializeEditor: _initializeEditor } ) => {
86-
_initializeEditor( {
87-
allowedBlockTypes: pluginLoadResult?.allowedBlockTypes,
88-
pluginLoadFailed: pluginLoadResult?.pluginLoadFailed,
89-
} );
90-
}
91-
);
109+
/**
110+
* Initialize the editor module. Lazy-loaded to ensure WordPress globals are
111+
* before importing the editor module and referencing `window.wp` globals.
112+
*
113+
* @param {Object} pluginLoadResult - Results from plugin loading
114+
* @return {Promise} Promise that resolves when the editor is initialized
115+
*/
116+
async function initializeEditor( pluginLoadResult = {} ) {
117+
const { initializeEditor: _initializeEditor } = await import( './editor' );
118+
_initializeEditor( {
119+
allowedBlockTypes: pluginLoadResult.allowedBlockTypes,
120+
pluginLoadFailed: pluginLoadResult.pluginLoadFailed,
121+
} );
92122
}
93123

124+
/**
125+
* Log and display an editor load error message.
126+
*
127+
* @param {Error} err - The error that occurred
128+
*/
94129
function handleError( err ) {
95130
error( 'Error initializing editor', err );
96131
const errorDetails = EditorLoadError( { error: err } );

0 commit comments

Comments
 (0)