Build: Add widgets/ folder support to @wordpress/build#77347
Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
There was a problem hiding this comment.
Pull request overview
Adds first-class widgets/ folder support to @wordpress/build, enabling discovery, bundling, and PHP registration of widget script modules following the established routes/ pattern.
Changes:
- Introduces widget discovery utilities (
discoverWidgets,findWidgetEntry,getAllWidgets). - Extends the build pipeline to bundle widget
renderandwidgetentries and generate widget PHP registry/registration files. - Updates templates/README and watch mode to include widgets.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/wp-build/templates/widget-registry.php.template | New PHP template to emit a widgets registry array. |
| packages/wp-build/templates/widget-registration.php.template | New PHP template to register widget script modules based on the registry. |
| packages/wp-build/templates/build.php.template | Loads widgets.php when generated. |
| packages/wp-build/lib/widget-utils.mjs | Implements widget discovery and entry resolution helpers. |
| packages/wp-build/lib/build.mjs | Adds widget build phase, PHP generation for widgets, and widget file watching. |
| packages/wp-build/README.md | Documents the experimental widgets feature and expected structure/output. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Size Change: 0 B Total Size: 7.82 MB ℹ️ View Unchanged
|
|
Flaky tests detected in ca25ed3. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/25157332759
|
Adds file-based widget discovery, dual-entry esbuild pipeline (render + widget), PHP registration templates, and watch mode.
ce363c0 to
87d097d
Compare
widgets/ folder support to @wordpress/build
Mirror the three-function shape of routes: getAllWidgets, getWidgetMetadata, getWidgetFiles. Drop PREFIX_UNDERSCORE and clean up esbuild options to match buildRoute.
Document the dual-entry pattern: widget.json for static discovery metadata, widget.ts for runtime schema with i18n and typed attributes. Include a minimal widget.ts in the hello-world fixture to exercise both entries.
- widget-utils: align extensions with SOURCE_EXTENSIONS, wrap JSON.parse in try/catch, drop unused hasStyle flag - build: sort widget registry entries by dirName for deterministic output - build: extract collectWidgets() helper shared by buildAll - README: scope widget.json field claims to what the registry currently forwards
Resolves TS implicit any error flagged by CI type checks.
Follows Copilot review feedback in #77347.
Follows Copilot review feedback in #77347.
Adds file-based widget discovery, dual-entry esbuild pipeline (render + widget), PHP registration templates, and watch mode.
Mirror the three-function shape of routes: getAllWidgets, getWidgetMetadata, getWidgetFiles. Drop PREFIX_UNDERSCORE and clean up esbuild options to match buildRoute.
Document the dual-entry pattern: widget.json for static discovery metadata, widget.ts for runtime schema with i18n and typed attributes. Include a minimal widget.ts in the hello-world fixture to exercise both entries.
- widget-utils: align extensions with SOURCE_EXTENSIONS, wrap JSON.parse in try/catch, drop unused hasStyle flag - build: sort widget registry entries by dirName for deterministic output - build: extract collectWidgets() helper shared by buildAll - README: scope widget.json field claims to what the registry currently forwards
Resolves TS implicit any error flagged by CI type checks.
Follows Copilot review feedback in #77347.
Follows Copilot review feedback in #77347.
…utenberg into add/wp-build-widgets-support
Follows Copilot review feedback in #77347.
Follows Copilot review feedback in #77347.
- Outputs notice in console - Adds notice to README.md
|
Added console log note when the script is building widgets. The note doesn't appear when widgets aren't built:
Note that the line gets logged each time anyone builds Gutenberg, so it might be worth showing a note only when WP Build is used outside Gutenberg, where experimental things are known/expected. Also added more visible notice in the readme using alerts GH-markdown syntax:
|
Follows Copilot review feedback in #77347.
Follows Copilot review feedback in #77347.
simison
left a comment
There was a problem hiding this comment.
Been playing with this for a couple of days, and it feels good.
We chatted with @youknowriad and they recommended marking tool with notice as experimental. Let's continue iterating with trunk.


What?
Adds an experimental widget build pipeline to
@wordpress/build, following the same file-based discovery pattern as the existingroutes/support. This enables plugins to define widgets in awidgets/directory and have them automatically discovered, bundled, and registered as WordPress script modules.Closes #77576
Needed for #77616
Related: #74542
Why?
WordPress needs a standardized way to build and register widget-type components (self-contained UI units with metadata). This mirrors the pattern already established for routes and blocks, giving plugin authors a consistent file-based convention for widgets.
How?
New file:
lib/widget-utils.mjsgetAllWidgets( rootDir )— returns widget directory names fromwidgets/getWidgetMetadata( rootDir, widgetName )— reads and validateswidget.jsongetWidgetFiles( widgetDirectory )— detectsrender.*,widget.*, andrender.scssentriesModified:
lib/build.mjsbuildWidget( widgetName )— dual-entry esbuild:render.min.js(ESM, with CSS) +widget.min.js(ESM), both with minified and non-minified variantsbuildAllWidgets()— discovers and builds all widgets in parallelgenerateWidgetRegistry( widgets, replacements )— generatesbuild/widgets/registry.phpgenerateWidgetsPhp( widgets, replacements )— generatesbuild/widgets.phpbuildAll()— wired widget build phase + PHP generationwatchMode()— added chokidar watcher forwidgets/*/filesNew templates:
widget-registry.php.template— PHP array with widget metadatawidget-registration.php.template— registers script modules viawp_register_script_module()Modified:
build.php.templatewidgets.phpWhy two entries per widget?
Widgets use a dual-entry pattern similar to how blocks split
block.jsonandedit.js:widget.json— static fields the host can filter, list, or register server-side before any JS loads (name,category,description).widget.ts— runtime schema that requires JS: translated strings via__(), typed attribute definitions coupled to therendercomponent's props,exampledata. The build injects therender_modulehandle automatically.This split avoids forcing the host to load every widget's JS just to enumerate what widgets exist, while preserving i18n and TypeScript type-safety for widgets that opt into attributes. See
packages/wp-build/README.md→ "Why two entries?" for the full rationale and a worked example.Architecture parallel with routes
Testing Instructions
This PR ships a minimal
widgets/hello-world/fixture at the repo root that exercises the pipeline end-to-end.npm run build(ornpx wp-build)🧩 Phase 4: Building widgets...followed by✔ Built widget hello-worldbuild/widgets/hello-world/render.min.js+render.jsbuild/widgets/hello-world/render.min.asset.phpbuild/widgets/registry.php— contains entry with'name' => 'wordpress/hello-world','has_render' => true,'has_widget' => falsebuild/widgets.php— definesgutenberg_register_widget_modules()and registerswp/widgets/hello-world/renderas a script modulebuild/build.phpconditionally requireswidgets.phpnpx wp-build --watch, editwidgets/hello-world/render.tsx, confirm only that widget rebuildsThe
widgets/hello-world/fixture is deliberately minimal and can be removed or replaced by consumers once their own widgets are defined.