Skip to content

Commit d8890f1

Browse files
perf: lazy-load AI Content Planner from the block editor
The 27.6 release pushed the plugin zip past the 5 MB mark. The dominant cause was the AI Content Planner module being bundled twice: * statically imported from packages/js/src/initializers/block-editor-integration.js and the Sidebar/Metabox fills, so it landed in block-editor.js (+161 KB raw / +28 KB gzipped); * shipped a second time as the standalone js/dist/ai-content-planner.js entry (~177 KB raw / 54 KB gzipped). That bundle's initialize.js only exports initContentPlanner() and never self-bootstraps, so on Elementor and classic-editor pages — where the standalone bundle was the only one enqueued — the planner UI never actually mounted. The bundle was dead weight there. Convert the three import sites to dynamic import() / React.lazy + Suspense under a single webpackChunkName, then drop the standalone webpack entry and localize wpseoContentPlanner onto block-editor instead. The Elementor enqueue hook is removed too: it was only ever shipping the dead-weight bundle. Net js/dist gzipped delta vs 27.6: -25.8 KiB. The planner code now ships once, on demand, and block-editor.js is ~28 KiB gzipped lighter for users without the AI feature enabled. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6eb2a30 commit d8890f1

5 files changed

Lines changed: 28 additions & 14 deletions

File tree

config/webpack/paths.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ const getEntries = ( sourceDirectory = "./packages/js/src" ) => ( {
5353
workouts: `${ sourceDirectory }/workouts.js`,
5454
"frontend-inspector-resources": `${ sourceDirectory }/frontend-inspector-resources.js`,
5555
"ai-generator": `${ sourceDirectory }/ai-generator/initialize.js`,
56-
"ai-content-planner": `${ sourceDirectory }/ai-content-planner/initialize.js`,
5756
"ai-consent": `${ sourceDirectory }/ai-consent/initialize.js`,
5857
plans: `${ sourceDirectory }/plans/initialize.js`,
5958
} );

packages/js/src/components/fills/MetaboxFill.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* External dependencies */
22
import { useSelect } from "@wordpress/data";
3-
import { Fragment } from "@wordpress/element";
3+
import { Fragment, lazy, Suspense } from "@wordpress/element";
44
import { Fill } from "@wordpress/components";
55
import { __ } from "@wordpress/i18n";
66
import PropTypes from "prop-types";
@@ -24,9 +24,14 @@ import { BlackFridayPromotion } from "../BlackFridayPromotion";
2424
import { withMetaboxWarningsCheck } from "../higherorder/withMetaboxWarningsCheck";
2525
import isBlockEditor from "../../helpers/isBlockEditor";
2626
import useToggleMarkerStatus from "./hooks/useToggleMarkerStatus";
27-
import ContentPlannerEditorItem from "../../ai-content-planner/containers/content-planner-editor-item";
2827
import { EditorIntro } from "../EditorIntro";
2928

29+
// Lazy-loaded so the planner module is not bundled into block-editor.js.
30+
const ContentPlannerEditorItem = lazy( () => import(
31+
/* webpackChunkName: "ai-content-planner-editor" */
32+
"../../ai-content-planner/containers/content-planner-editor-item"
33+
) );
34+
3035
const BlackFridayPromotionWithMetaboxWarningsCheck = withMetaboxWarningsCheck( BlackFridayPromotion );
3136

3237
/* eslint-disable complexity */
@@ -77,7 +82,9 @@ export default function MetaboxFill( { settings } ) {
7782
</SidebarItem>
7883
) }
7984
{ isPost && isBlockEditorActive && isAiFeatureActive && <SidebarItem key="content-planner" renderPriority={ 2 }>
80-
<ContentPlannerEditorItem location="metabox" />
85+
<Suspense fallback={ null }>
86+
<ContentPlannerEditorItem location="metabox" />
87+
</Suspense>
8188
</SidebarItem> }
8289
{ settings.isKeywordAnalysisActive && <SidebarItem key="keyword-input" renderPriority={ 8 }>
8390
<KeywordInput

packages/js/src/components/fills/SidebarFill.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* External dependencies */
22
import { Fill } from "@wordpress/components";
3-
import { Fragment } from "@wordpress/element";
3+
import { Fragment, lazy, Suspense } from "@wordpress/element";
44
import PropTypes from "prop-types";
55
import { __ } from "@wordpress/i18n";
66
import { get } from "lodash";
@@ -23,9 +23,14 @@ import WincherSEOPerformanceModal from "../../containers/WincherSEOPerformanceMo
2323
import KeywordUpsell from "../modals/KeywordUpsell";
2424
import isBlockEditor from "../../helpers/isBlockEditor";
2525
import useToggleMarkerStatus from "./hooks/useToggleMarkerStatus";
26-
import ContentPlannerEditorItem from "../../ai-content-planner/containers/content-planner-editor-item";
2726
import { EditorIntro } from "../EditorIntro";
2827

28+
// Lazy-loaded so the planner module is not bundled into block-editor.js.
29+
const ContentPlannerEditorItem = lazy( () => import(
30+
/* webpackChunkName: "ai-content-planner-editor" */
31+
"../../ai-content-planner/containers/content-planner-editor-item"
32+
) );
33+
2934
/* eslint-disable complexity */
3035
/**
3136
* Creates the SidebarFill component.
@@ -66,7 +71,9 @@ export default function SidebarFill( { settings } ) {
6671
</EditorIntro>
6772
</SidebarItem>
6873
{ isPost && isBlockEditorActive && isAiFeatureActive && <SidebarItem key="content-planner" renderPriority={ 2 }>
69-
<ContentPlannerEditorItem location="sidebar" />
74+
<Suspense fallback={ null }>
75+
<ContentPlannerEditorItem location="sidebar" />
76+
</Suspense>
7077
</SidebarItem> }
7178
{ settings.isKeywordAnalysisActive && <SidebarItem key="keyword-input" renderPriority={ 8 }>
7279
<KeywordInput

packages/js/src/initializers/block-editor-integration.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import SidebarFill from "../containers/SidebarFill";
2727
import WincherPostPublish from "../containers/WincherPostPublish";
2828
import { isAnnotationAvailable } from "../decorator/gutenberg";
2929
import { link } from "../inline-links/edit-link";
30-
import initContentPlanner from "../ai-content-planner/initialize";
3130
import { getIsAiFeatureEnabled } from "../redux/selectors/preferences";
3231

3332
/**
@@ -211,7 +210,11 @@ export default function initBlockEditorIntegration( store ) {
211210
registerFormats();
212211
initializeAnnotations( store );
213212
if ( getIsAiFeatureEnabled() ) {
214-
initContentPlanner();
213+
// Lazy-loaded so the planner module is not bundled into block-editor.js.
214+
import(
215+
/* webpackChunkName: "ai-content-planner-editor" */
216+
"../ai-content-planner/initialize"
217+
).then( ( { "default": initContentPlanner } ) => initContentPlanner() );
215218
}
216219

217220
const yoastTab = getQueryArg( window.location.href, "yoast-tab" );

src/ai/content-planner/user-interface/content-planner-integration.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,6 @@ public function __construct(
6161
*/
6262
public function register_hooks() {
6363
\add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
64-
// Enqueue after Elementor_Premium integration, which re-registers the assets.
65-
\add_action( 'elementor/editor/before_enqueue_scripts', [ $this, 'enqueue_assets' ], 11 );
6664
}
6765

6866
/**
@@ -77,12 +75,12 @@ public function get_script_data(): array {
7775
}
7876

7977
/**
80-
* Localizes the content planner script data.
78+
* Localizes the content planner script data onto the block-editor bundle,
79+
* which lazy-loads the planner module on demand.
8180
*
8281
* @return void
8382
*/
8483
public function enqueue_assets() {
85-
$this->asset_manager->enqueue_script( 'ai-content-planner' );
86-
$this->asset_manager->localize_script( 'ai-content-planner', 'wpseoContentPlanner', $this->get_script_data() );
84+
$this->asset_manager->localize_script( 'block-editor', 'wpseoContentPlanner', $this->get_script_data() );
8785
}
8886
}

0 commit comments

Comments
 (0)