Review/multiple plugins#14
Merged
troychaplin merged 11 commits intomainfrom Apr 18, 2026
Merged
Conversation
Remove public API surfaces that had no consumers in the workspace and no contracts beyond historical intent. ~260 LOC removed; no active consumer breaks. - Delete Meta\Validator class (server-side validation helper) - Delete Contracts\CheckProvider interface (no implementations) - Remove Block\Registry::unregister_check() + wp_validation_check_unregistered - Remove Block\Registry::set_check_enabled() + wp_validation_check_toggled - Remove Editor\Registry::register_editor_check_for_post_types() bulk helper - Remove EditorDetection get_current_screen() fallback (unreachable) - Remove orphaned wp_validation_validate_meta filter - Update technical/hooks.md, technical/api.md, technical/decisions.md, guide/README.md, guide/meta-checks.md, guide/examples.md, docs/README.md, CLAUDE.md, INTEGRATION.md, PROPOSAL.md - Delete guide/check-providers.md Lifecycle hooks kept (documented public API, consumed by settings addon or reserved): wp_validation_initialized, wp_validation_ready, wp_validation_editor_checks_ready, wp_validation_check_registered, wp_validation_editor_check_registered, wp_validation_meta_check_registered, wp_validation_check_args, wp_validation_editor_check_args, wp_validation_meta_check_args, wp_validation_should_register_check, wp_validation_should_register_editor_check, wp_validation_should_register_meta_check, wp_validation_check_level. See docs/gutenberg-alignment/pass-c.md for rationale. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 58-LOC I18n class was a wrapper around a single wp_set_script_translations() call. Per Gutenberg-style functional preference, collapse into Assets.php at the enqueue site. - Delete includes/Core/I18n.php - Assets::__construct() now accepts (string $plugin_file, string $text_domain) - Assets::enqueue_block_assets() calls wp_set_script_translations() inline - Plugin::init() drops init_translations() step; passes text_domain directly to the Assets constructor - Update CLAUDE.md project structure diagram No public API changes. See docs/gutenberg-alignment/pass-a.md Batch 3 / pass-c.md C-7 for rationale. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pull duplicated registration logic (defaults merge, required-field check, level validation, warning_msg fallback, namespace stamping, priority sort, wp_validation_check_level filter application) into a shared abstract base class. Concrete subclasses keep their own singleton, storage shape, scope-specific register method, and scope-specific hook names. - Add includes/AbstractRegistry.php with shared helpers: normalize_args(), stamp_namespace(), sort_by_priority(), apply_level_filter(), plus DEFAULTS + VALID_LEVELS constants - Block, Meta, Editor registries now extend AbstractRegistry - Logger trait methods changed from private to protected so subclasses and the abstract can share inherited access Behavior change (improvement): priority validation now applies to all three scopes. Previously only Block validated priority; Meta and Editor silently accepted garbage values. Non-numeric priorities now coerce to 10 uniformly. Public API unchanged: global function signatures, filter names, action hook names, REST response shape all identical. Constants declared without visibility modifiers for PHP 7.0 compatibility (visibility on class constants requires PHP 7.1+; plugin header declares 7.0 minimum). See docs/gutenberg-alignment/pass-c.md C-8 for rationale. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Stop squatting on the core-reserved wp/v2 namespace. Use a plugin-owned namespace now; the core-PR can negotiate a final location (wp/v2 or wp-block-editor/v1) during review. - ChecksController::$namespace: wp/v2 -> wp-validation/v1 - ChecksController::$rest_base: validation-checks -> checks - Final URL: /wp-json/wp-validation/v1/checks - Update docs: CLAUDE.md, README.md, readme.txt, docs/technical/README.md, docs/technical/api.md, docs/technical/companion-package.md, docs/INTEGRATION.md, docs/PROPOSAL.md, docs/guide/README.md, docs/guide/examples.md - PROPOSAL.md calls out that final core namespace is TBD during PR Settings addon updated in a separate commit in its own repo. See docs/gutenberg-alignment/pass-a.md Batch 2 for rationale. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Flatten src/editor/* and src/shared/* into a Gutenberg-package-style tree
so the eventual core-PR lands as a git mv into packages/validation/src/.
All public API surfaces (store name, filter names, global PHP functions,
REST endpoint) are unchanged; this is a pure reorganization plus a few
architectural polish items.
Structure:
src/index.js Entry point (replaces src/script.js)
src/store/ Data store (moved verbatim from src/editor/store/)
src/utils/ Flat utilities (validate-*, use-invalid-*,
use-meta-*, issue-helpers, get-validation-config,
use-debounced-validation, use-validation-issues,
index.js barrel)
src/components/ One folder per component
src/hooks/ Side-effect modules + two React hooks
(use-validation-sync, use-validation-lifecycle)
Architectural changes (Pass B):
- ValidationProvider renderless component becomes useValidationSync hook
- ValidationAPI renderless component becomes useValidationLifecycle hook
- Both hooks invoked by renderless sibling wrappers (<ValidationSync />,
<ValidationLifecycle />) inside the ValidationPlugin root to avoid the
infinite render loop that arose from subscribing and dispatching to the
same store within one parent component
- New src/hooks/pre-save-validation.js installs the editor.preSavePost
async filter as a belt-and-suspenders save gate on top of lockPostSaving
Consolidations (Pass C):
- Extracted useValidationIssues() hook (new src/utils/) to deduplicate
the 3-selector useSelect block from ValidationSidebar and the
lifecycle hook
- use-meta-field.js collapses its dual useSelect into one
Getter-style hook files renamed: GetInvalid* -> useInvalid* both in
file names and exported function names, reflecting their nature as
React hooks rather than plain getters.
Build/tooling:
- webpack.config.js: entry points to src/index.js; path aliases
(@, @editor, @shared) removed in favour of relative imports
- package.json: sideEffects declared for src/index.js, src/hooks/**,
src/store/index.js, src/**/*.scss; main updated to
build/validation-api.js
HOC behavior unchanged:
- editor.BlockEdit filter still registers withErrorHandling (now lives
in src/hooks/validate-block.js as a side-effect module)
- editor.BlockListBlock filter still registers withBlockValidationClasses
(now in src/hooks/block-validation-classes.js)
Completes the five-batch Gutenberg alignment plan. See
docs/gutenberg-alignment/consolidated-plan.md and pass-a.md for the full
rationale; pass-b.md for the hook conversion and preSavePost decisions;
pass-c.md for the consolidation rationale.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Match Gutenberg package convention of showing realistic usage snippets on public selectors/actions and user-facing hooks. Improves IDE hover documentation for external plugin authors. - store/selectors.js: 6 selectors (getInvalidBlocks, getInvalidMeta, getInvalidEditorChecks, getBlockValidation, hasErrors, hasWarnings) each showing a useSelect pattern - store/actions.js: 5 actions (setInvalidBlocks, setInvalidMeta, setInvalidEditorChecks, setBlockValidation, clearBlockValidation) each showing a useDispatch pattern - utils/use-meta-field.js: TextControl spread example - utils/use-meta-validation.js: custom render + usage-vs-useMetaField note Internal helpers (issue-helpers, validate-*) keep their one-line @param/@return style — consumed by the framework itself, not by external plugins. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rename src/store/constants.js to constants.ts and add type definitions for the store state, validation issues, and block/meta validation results. Action type constants use `as const` for narrower literal types, enabling exhaustive switch checks in the reducer. Other modules continue running as JavaScript via @babel/preset-typescript; they gain IDE type inference on imports from constants.ts without requiring .ts migration themselves. Delete stale babel.config.json (pre-TS two-preset config from March) that was shadowing @wordpress/babel-preset-default and blocking TS syntax handling. wp-scripts' default preset now applies as designed and includes @babel/preset-typescript out of the box. Incidental bundle-size drop from ~94KB to ~70KB (minified) from the more optimized preset configuration. New exported types: - ValidationMode, IssueType - ValidationIssue - BlockValidationResult - MetaValidationResult - State No runtime behavior change. Matches Gutenberg package convention of a typed store/constants file. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cover the pure logic layer — store reducer, action creators, selectors, and the issue-helper factories — with Jest. No mocks required; these are all pure functions consuming plain inputs. Test infrastructure is zero-config via @wordpress/scripts (test-unit-js uses its bundled Jest preset). Added `test` and `test:watch` npm scripts and an .eslintrc override so Jest globals (describe/it/expect) don't trip no-undef in __tests__ directories. Coverage: - src/store/__tests__/reducer.test.js — default state, all 5 action types, unknown-action pass-through, CLEAR_BLOCK_VALIDATION no-op edge case - src/store/__tests__/actions.test.js — each action creator's shape - src/store/__tests__/selectors.test.js — all 6 selectors including error-precedence logic in hasErrors/hasWarnings and DEFAULT_BLOCK_RESULT fallback - src/utils/__tests__/issue-helpers.test.js — all 8 exports, including the PHP-style snake_case to camelCase transform in createIssue Deferred to a future polish pass (need filter/store mocking): - validate-block, validate-meta, validate-editor - Custom hooks (useMetaField, useMetaValidation, useInvalid* hooks) - Integration / e2e tests via @wordpress/env Run with `pnpm test` (~1s). 56/56 pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mark Polish 1-4 done with commit hashes in the consolidated plan. Add deferred-polish list (5, 5b, 6, 7) with explicit scope, prerequisites, and target file paths so the next iteration can pick them up cold. Rewrite TODO.md to reflect the current tree: - "Completed" now covers naming alignment + five-batch plan + post-batch polish 1-4 (each with commit hashes) - "Remaining" restructured into Testing / Performance / TypeScript / Future considerations, with file paths updated to post-Batch-1 locations (src/hooks/use-validation-*.js, src/store/__tests__/, etc.) - Cross-links to the consolidated plan for authoritative polish status Plugin is now in the state described for picking up integration-plugin work before cycling back to draft the Gutenberg PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…state Comprehensive doc pass after the alignment + polish work shipped: - Removed stale planning artifacts whose purpose has been fulfilled - Rewrote docs that still described pre-Batch-1 architecture - Added two new docs: a troubleshooting guide for integrating plugins and a single-page PR-readiness summary for future-you / collaborators Deleted (git history preserves them): - docs/gutenberg-alignment/pass-a.md (convention audit — work complete) - docs/gutenberg-alignment/pass-b.md (architectural audit — work complete) - docs/gutenberg-alignment/pass-c.md (leanness audit — work complete) Rewrote (fixed stale architecture references): - CLAUDE.md: project-structure tree, data-flow diagram, conventions, doc map. Removed src/editor/ + src/shared/ + webpack aliases + renderless-component descriptions. Added AbstractRegistry, hooks (useValidationSync/useValidationLifecycle), editor.preSavePost gate, test + polish script references, TypeScript constants.ts note. - docs/technical/README.md: JS Layer section rewritten for the hook- first architecture. Side-effect modules in src/hooks/ listed with their filter registrations. Render-loop sibling-wrapper rationale documented. Save-locking defense-in-depth explained. - docs/technical/data-flow.md: full 14-step walkthrough from PHP registration through useInvalidBlocks -> useValidationSync -> store -> useValidationLifecycle / pre-save-validation / sidebar. Covers AbstractRegistry normalization steps. - docs/gutenberg-alignment/README.md: points at the three still-useful files (consolidated-plan, core-pr-migration, PR-READINESS). - docs/gutenberg-alignment/consolidated-plan.md: pared down from execution playbook to execution record. Per-batch commits + summary. Polish status with commit hashes. Deferred items (5, 5b, 6, 7) with scope and prerequisites. Updated (targeted fixes): - docs/INTEGRATION.md: Component Mapping now lists hooks not renderless components; Packages Affected section reflects hook-first architecture; editor.preSavePost added to "new to Gutenberg" table; open questions refreshed. - docs/PROPOSAL.md: Reference-Implementation section describes the current hook-based design. Added note about the sibling-wrapper pattern. State-management description updated. - docs/README.md (index): adds PR-READINESS + troubleshooting; reorders into Start-Here / Developer-Guide / Technical / Core-Merge / Working- Notes sections. Created: - docs/PR-READINESS.md: single-page "where are we, what's next" for the Gutenberg PR. TL;DR, status tables, open questions for core team, next-steps checklist for future-you returning after a break. - docs/guide/troubleshooting.md: 10+ common issues developers integrating with the API hit, each with diagnostic steps — sidebar empty, validation not firing, REST 401/404, borders missing, save stuck locked, JSON response errors, React #185 render loops, env-specific failures. Build + tests clean (pnpm build + pnpm test both green). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request
Description
Wholesale Gutenberg-alignment refactor + post-batch polish pass. Brings the plugin's internal architecture, file layout, naming, and tests in line with current Gutenberg package conventions, preparing the codebase for a future core-merge proposal. Public API (global PHP functions, JS filter names, store name, registration hooks) is preserved — existing consumers work without code changes, with the single exception of the REST endpoint path (coordinated with the only consumer,
validation-api-settings, in the same commit).Represents 11 commits across five alignment batches, a four-item polish pass, and a documentation refresh. See docs/gutenberg-alignment/consolidated-plan.md for the full execution record and docs/PR-READINESS.md for where this work sits relative to a future Gutenberg core PR.
Type of Change
Related Issues
Related issue(s): N/A — internal alignment work. Tracked in docs/gutenberg-alignment/consolidated-plan.md.
Changes Made
Five-batch alignment plan:
src/{store, utils, hooks, components}/. RenderlessValidationProvider/ValidationAPIconverted touseValidationSync+useValidationLifecyclehooks (invoked from sibling renderless wrappers to avoid render loops).editor.preSavePostsave-time safety gate added.useValidationIssues()consolidated hook extracted.useMetaFielddualuseSelectcollapsed.getInvalid*renamed touseInvalid*. Webpack aliases dropped.package.jsonsideEffectsdeclared.wp/v2/validation-checks(reserved core namespace) to plugin-ownedwp-validation/v1/checks. Final core namespace deferred to PR review. Settings addon updated in lockstep.includes/Core/I18n.php; inlinedwp_set_script_translations()inCore/Assets.php. 58 LOC removed.Meta\Validatorclass,Contracts/CheckProviderinterface,Block\Registry::unregister_check()+set_check_enabled(),Editor\Registry::register_editor_check_for_post_types(),EditorDetection::get_current_screen()fallback, two orphan actions, one orphan filter. Companion integration-example hot-fix: restored meta-field border styling that a prior commit had stripped.ValidationAPI\AbstractRegistrybase class. Block/Meta/Editor registries now extend it. Shared defaults merge, required-field validation,warning_msgfallback, level validation,namespacestamping, priority sort, andwp_validation_check_levelfilter application. Priority validation now consistent across all three scopes (was Block-only).Polish pass:
@exampleJSDoc blocks on 13 public-API entries (6 store selectors, 5 store actions,useMetaField,useMetaValidation)src/store/constants.ts(typedState,ValidationIssue,BlockValidationResult,MetaValidationResult,ValidationMode,IssueType); deleted stalebabel.config.jsonthat was shadowing@wordpress/scripts' default presetissue-helpers. Run withpnpm test.Documentation refresh:
CLAUDE.md,docs/technical/README.md,docs/technical/data-flow.md,docs/gutenberg-alignment/consolidated-plan.mdto reflect the post-Batch-1 architecturedocs/PR-READINESS.md(single-page "where are we on the Gutenberg PR" overview) anddocs/guide/troubleshooting.md(10+ common integration issues with diagnostics)docs/PROPOSAL.md,docs/INTEGRATION.md,docs/README.mdfor consistency with the current implementationTesting
Environment:
Test Cases:
For new developer API features:
For developer API updates:
General testing:
Automated verification:
pnpm test— 56/56 tests pass in ~1spnpm lint:js— cleanpnpm lint:php— clean (PHPCS, 0 violations)pnpm lint:css— cleanpnpm build— succeeds, produces expectedbuild/validation-api.{js,css,asset.php}Manual verification per batch: Each batch was manually verified in a running WordPress instance before commit per the acceptance criteria documented in docs/gutenberg-alignment/consolidated-plan.md. Covered: editor loads cleanly, validation sidebar renders, block/meta/editor checks fire, border classes apply, publish locking toggles correctly,
editor.preSavePostaborts saves when errors exist, settings addon round-trips overrides, REST endpoint returns expected response shape.Screenshots
N/A — internal refactor with no user-facing UI changes. All visible behaviors (sidebar rendering, block indicators, meta-field borders, publish locking) verified unchanged against the pre-refactor baseline.
Developer API Impact (if applicable)
Detail on the REST path change:
The REST endpoint moved from
wp/v2/validation-checkstowp-validation/v1/checksto stop squatting on the core-reservedwp/v2/*namespace. This is a breaking change for any third-party consumer hitting the endpoint directly, but the only known consumer (thevalidation-api-settingscompanion plugin) is updated in the same commit set. The final core-facing namespace (wp/v2/validation-checks,wp-block-editor/v1/validation-checks, or similar) is deferred to Gutenberg core review.Checklist