From 3c75deaf3217e8aa18c9d30b35a424516f7ec27f Mon Sep 17 00:00:00 2001 From: Aryan Jasala Date: Fri, 2 Oct 2026 04:30:46 +0530 Subject: [PATCH] docs: refresh setup notes and link wp-primitives v2.0.0 - Link the wp-primitives v2.0.0 docs; composer.json requires ^2.0. - Dependencies come from Packagist and npm, not GitHub. - npm install adds a pre-commit hook as well as the commit-message hook. - wp-env activates the plugin itself; the tests use .wp-env.tests.json. - An initialized plugin has no Jest tests until one is added. - .nvmrc 22.22.2 and engines.node match @wordpress/scripts 36. --- .nvmrc | 2 +- DEVELOPMENT.md | 44 ++++++++++++++++++------------------ README.md | 2 +- docs/blocks-and-assets.md | 4 ++-- docs/features.md | 20 ++++++++-------- docs/getting-started.md | 10 ++++---- docs/internal/maintenance.md | 2 +- docs/local-development.md | 6 +++-- docs/scaffolding.md | 4 ++-- package-lock.json | 3 +++ package.json | 3 +++ 11 files changed, 55 insertions(+), 45 deletions(-) diff --git a/.nvmrc b/.nvmrc index 2bd5a0a..db49bb1 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22 +22.22.2 diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 8f18031..243d76c 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -4,7 +4,7 @@ This guide shows how to extend the plugin by hand: where new code belongs, which Run commands from the plugin directory. The examples use an initialized project named **Acme Content**: namespace `Acme_Content\Features`, tests namespace `Acme_Content\Features\Tests`, text domain `acme-content-features`. Replace them with the values in your own `composer.json` and plugin header. -The plugin sits on two layers. `vendor/rtcamp/wp-primitives/` is the upstream [framework](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/index.md): the registration system, abstract base classes, loaders and utilities, installed by Composer. `inc/` is everything project-specific. Never edit `vendor/`; it is overwritten on the next `composer install`, and framework changes belong in the framework repository. +The plugin sits on two layers. `vendor/rtcamp/wp-primitives/` is the upstream [framework](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/index.md): the registration system, abstract base classes, loaders and utilities, installed by Composer. `inc/` is everything project-specific. Never edit `vendor/`; it is overwritten on the next `composer install`, and framework changes belong in the framework repository. ## Before adding code @@ -30,9 +30,9 @@ Every PHP file starts with `declare( strict_types = 1 );`, uses full parameter a 3. The `Loader` instantiates each class. If it implements `Registrable`, it calls `register_hooks()`; a `ConditionallyRegistrable` class is skipped when `can_register()` returns false. If it implements `Shareable`, the instance is kept in `Main`'s container for `get_shared()`. 4. A module is itself `Registrable`: its `register_hooks()` loads the classes from its `get_classes()` the same way. -A class that is not reachable from `Main::CLASSES` never runs, even if it autoloads. The framework's [architecture overview](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/architecture.md) explains the loop and [when hooks actually fire](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/architecture.md#when-hooks-actually-fire). +A class that is not reachable from `Main::CLASSES` never runs, even if it autoloads. The framework's [architecture overview](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/architecture.md) explains the loop and [when hooks actually fire](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/architecture.md#when-hooks-actually-fire). -**Do not default to `Singleton`.** It is for `Main` only. Use a plain `Registrable` loaded by a module; add `Shareable` only when another class must retrieve the instance (see [sharing vs. singletons](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/architecture.md#sharing-vs-singletons)). +**Do not default to `Singleton`.** It is for `Main` only. Use a plain `Registrable` loaded by a module; add `Shareable` only when another class must retrieve the instance (see [sharing vs. singletons](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/architecture.md#sharing-vs-singletons)). ## The module pattern @@ -58,21 +58,21 @@ final class PostTypes extends AbstractModule { | Feature | Extends / implements | Framework reference | | --- | --- | --- | -| Custom post type | `AbstractPostType` | [abstracts](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/abstracts.md#abstractposttype) | -| Taxonomy | `AbstractTaxonomy` | [abstracts](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/abstracts.md#abstracttaxonomy) | -| Dynamic block rendered by a class | `AbstractBlock` (see [Blocks and assets](docs/blocks-and-assets.md#blocks)) | [abstracts](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/abstracts.md#abstractblock) | -| Shortcode | `AbstractShortcode` | [abstracts](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/abstracts.md#abstractshortcode) | -| REST controller | `AbstractRESTController` | [abstracts](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/abstracts.md#abstractrestcontroller) | -| Settings page | `AbstractSettingsPage` | [abstracts](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/abstracts.md#abstractsettingspage) | -| Other admin page | `AbstractAdminPage` | [abstracts](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/abstracts.md#abstractadminpage) | -| User role | `AbstractUserRole` | [abstracts](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/abstracts.md#abstractuserrole) | -| Behaviour behind a runtime feature flag | `AbstractFeature` (not used by the skeleton yet) | [abstracts](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/abstracts.md#abstractfeature) | -| WP-CLI command | `CLICommand` interface, listed in `CLI::get_commands()` | [contracts](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/contracts.md#clicommand) | -| Cron job, cache or transient wrapper, integration — anything that just wires hooks | `Registrable` interface (see `ExampleCronJob`, `ExampleCache`) | [contracts](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/contracts.md#registrable) | -| Same, but only in some contexts | `ConditionallyRegistrable` interface | [contracts](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/contracts.md#conditionallyregistrable) | -| A service other classes retrieve | `Registrable` + `Shareable`, listed in `Main::CLASSES` | [contracts](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/contracts.md#shareable) | - -Never call `register_post_type()`, `register_taxonomy()`, `register_rest_route()` outside a controller, `add_menu_page()`, `add_shortcode()` or `register_block_type()` directly from a feature class: extend the matching abstract. The framework's [hook table](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/abstracts.md#which-hook-each-one-uses) shows which WordPress hook each abstract uses. +| Custom post type | `AbstractPostType` | [abstracts](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/abstracts.md#abstractposttype) | +| Taxonomy | `AbstractTaxonomy` | [abstracts](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/abstracts.md#abstracttaxonomy) | +| Dynamic block rendered by a class | `AbstractBlock` (see [Blocks and assets](docs/blocks-and-assets.md#blocks)) | [abstracts](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/abstracts.md#abstractblock) | +| Shortcode | `AbstractShortcode` | [abstracts](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/abstracts.md#abstractshortcode) | +| REST controller | `AbstractRESTController` | [abstracts](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/abstracts.md#abstractrestcontroller) | +| Settings page | `AbstractSettingsPage` | [abstracts](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/abstracts.md#abstractsettingspage) | +| Other admin page | `AbstractAdminPage` | [abstracts](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/abstracts.md#abstractadminpage) | +| User role | `AbstractUserRole` | [abstracts](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/abstracts.md#abstractuserrole) | +| Behaviour behind a runtime feature flag | `AbstractFeature` (not used by the skeleton yet) | [abstracts](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/abstracts.md#abstractfeature) | +| WP-CLI command | `CLICommand` interface, listed in `CLI::get_commands()` | [contracts](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/contracts.md#clicommand) | +| Cron job, cache or transient wrapper, integration — anything that just wires hooks | `Registrable` interface (see `ExampleCronJob`, `ExampleCache`) | [contracts](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/contracts.md#registrable) | +| Same, but only in some contexts | `ConditionallyRegistrable` interface | [contracts](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/contracts.md#conditionallyregistrable) | +| A service other classes retrieve | `Registrable` + `Shareable`, listed in `Main::CLASSES` | [contracts](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/contracts.md#shareable) | + +Never call `register_post_type()`, `register_taxonomy()`, `register_rest_route()` outside a controller, `add_menu_page()`, `add_shortcode()` or `register_block_type()` directly from a feature class: extend the matching abstract. The framework's [hook table](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/abstracts.md#which-hook-each-one-uses) shows which WordPress hook each abstract uses. ## Add a post type @@ -191,7 +191,7 @@ protected function get_classes(): array { } ``` -Rerun the test, then check the site: `npm run wp-env run cli -- wp post-type list` includes `book`, and **Books** appears in the admin menu. `AbstractPostType` registers on `init`; override `get_editor_supports()`, `get_custom_options()` and the other methods listed in the [post type reference](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/abstracts.md#abstractposttype) to change its behaviour. +Rerun the test, then check the site: `npm run wp-env run cli -- wp post-type list` includes `book`, and **Books** appears in the admin menu. `AbstractPostType` registers on `init`; override `get_editor_supports()`, `get_custom_options()` and the other methods listed in the [post type reference](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/abstracts.md#abstractposttype) to change its behaviour. ## Add a taxonomy @@ -282,7 +282,7 @@ $html = Util::templates()->get( 'content/book-card', null, [ 'book' => $post ] Anything shared without an accessor (`Assets`, `Components`) is retrieved with `Main::get_instance()->get_shared( Assets::class )`. -To add a shared service, create `inc/Core/.php` implementing `Shareable` (and `Registrable` if it hooks WordPress), add it to `Main::CLASSES` under the core services, and add a one-line accessor to `Util`. The framework utilities you can wrap this way are documented in [utilities](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/utilities.md): `Cache`, `Transients`, `Encryptor`, `Logger`, `FeatureSelector`, `Timer`. +To add a shared service, create `inc/Core/.php` implementing `Shareable` (and `Registrable` if it hooks WordPress), add it to `Main::CLASSES` under the core services, and add a one-line accessor to `Util`. The framework utilities you can wrap this way are documented in [utilities](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/utilities.md): `Cache`, `Transients`, `Encryptor`, `Logger`, `FeatureSelector`, `Timer`. ## Conditional registration @@ -321,9 +321,9 @@ Run the focused test, then the full checks in [Local development](docs/local-dev | --- | --- | | Class autoloads but nothing happens | Is it in its module's `get_classes()`, and is that module in `Main::CLASSES`? For CLI commands, is it in `CLI::get_commands()`? | | `Class not found` | The namespace must match the directory and the file name the class. Run `composer dump-autoload` after moving files. | -| Hook callback never runs | Check `register_hooks()` adds it, and that the hook has not already fired when the class loads (see [when hooks fire](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/architecture.md#when-hooks-actually-fire)). | +| Hook callback never runs | Check `register_hooks()` adds it, and that the hook has not already fired when the class loads (see [when hooks fire](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/architecture.md#when-hooks-actually-fire)). | | `get_shared()` fails | The class must implement `Shareable` and be listed in `Main::CLASSES` (a module's classes are not shared through `Main`). | | Post type or taxonomy missing | Run `wp post-type list` / `wp taxonomy list`; confirm registration and that the plugin is active. | | Composer dependencies not installed | The plugin shows an admin notice instead of a fatal error. Run `composer install` and reload. | -Keep framework API details in the [framework docs](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/index.md), and see [Included features](docs/features.md) for what the skeleton already provides. +Keep framework API details in the [framework docs](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/index.md), and see [Included features](docs/features.md) for what the skeleton already provides. diff --git a/README.md b/README.md index 7fad993..0620bf5 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ It builds on two shared rtCamp libraries and uses a third for CI: - [`@rtcamp/wp-tooling`](https://github.com/rtCamp/wp-tooling) (npm, development): the `npm run init` setup wizard, the `npx wp-tooling add` feature scaffolder, the shared lint configs and release scripts. - [`rtCamp/wp-shared-workflows`](https://github.com/rtCamp/wp-shared-workflows): the reusable lint, test and build CI that `.github/workflows/test-measure.yml` calls. -The skeleton documents what it provides and how to build on it. The framework's API and lifecycle are documented in [its own docs](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/index.md), and these guides link there where needed. +The skeleton documents what it provides and how to build on it. The framework's API and lifecycle are documented in [its own docs](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/index.md), and these guides link there where needed. > This is a **template**. `Project Name` / `project-name` / `Project_Name` are placeholders; `npm run init` renames them to your project. diff --git a/docs/blocks-and-assets.md b/docs/blocks-and-assets.md index 7b19dec..cafb39c 100644 --- a/docs/blocks-and-assets.md +++ b/docs/blocks-and-assets.md @@ -1,6 +1,6 @@ # Blocks and assets -How the plugin builds scripts, styles and blocks, and how each output reaches WordPress. For running the watchers see [Local development](local-development.md#edit-source-and-see-the-result); for the loader API see the framework's [`AssetLoader` reference](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/loaders.md#assetloader). +How the plugin builds scripts, styles and blocks, and how each output reaches WordPress. For running the watchers see [Local development](local-development.md#edit-source-and-see-the-result); for the loader API see the framework's [`AssetLoader` reference](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/loaders.md#assetloader). Names below use an initialized **Acme Content** project (`acme-content-features` text domain and handle prefix). @@ -66,7 +66,7 @@ There are two ways a block reaches WordPress, and the difference decides where y Choose a class when rendering needs plugin services, several templates, or unit tests of the render logic; `render.php` is enough for simple markup. Both kinds build from `src/blocks//` into `assets/build/blocks//`. Because rendered templates go through the template loader, a theme can override them from `/acme-content-features/block-templates/`. -`AbstractBlock`'s lifecycle and overridable methods (`get_block_dir()`, `get_block_args()`) are in the framework's [block reference](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/abstracts.md#abstractblock). +`AbstractBlock`'s lifecycle and overridable methods (`get_block_dir()`, `get_block_args()`) are in the framework's [block reference](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/abstracts.md#abstractblock). ### Create a block diff --git a/docs/features.md b/docs/features.md index e3f535b..64422e7 100644 --- a/docs/features.md +++ b/docs/features.md @@ -10,18 +10,18 @@ These stay in every project, whichever example sets you remove. Most are thin pl | Facility | Skeleton source | Framework piece | Default / how to use it | | --- | --- | --- | --- | -| Bootstrap and registration | `acme-content-features.php`, `inc/Autoloader.php`, `inc/Main.php` | `Singleton`, `Loader`, `Registrable`, `Container` ([architecture](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/architecture.md), [contracts](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/contracts.md)) | The entry file defines the `ACME_CONTENT_FEATURES_{FILE,VERSION,PATH,URL}` constants, loads Composer, and boots `Main`. Every loaded class is listed in `Main::CLASSES`. | -| Feature modules | `inc/Modules/.php` | `AbstractModule` ([abstracts](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/abstracts.md#abstractmodule), [modules](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/architecture.md#modules-loaders-that-hold-loaders)) | Each domain's module returns its concrete classes from `get_classes()`. See the [Development guide](../DEVELOPMENT.md#the-module-pattern). | -| Asset registration | `inc/Core/Assets.php`, `src/` | `AssetLoader` ([loaders](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/loaders.md#assetloader)) | Enqueues `main.js`/`main.css` on the frontend, registers `admin` and `editor` handles, registers `block.json` blocks and the Interactivity module. See [Blocks and assets](blocks-and-assets.md). | -| Templates | `inc/Core/Templates.php`, `templates/` | `TemplateLoader` ([loaders](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/loaders.md#templateloader)) | `Util::templates()->get( 'block-templates/example-block-dynamic' )`. A theme overrides a template by copying it to `/acme-content-features/`. | -| Components | `inc/Core/Components.php` | `ComponentLoader` ([loaders](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/loaders.md#componentloader)) | Loader only; no components ship. Put PHP in `src/components//.php` and assets in `src/css/components/` and `src/js/components/`. Resolve it with `Main::get_instance()->get_shared( Components::class )`. | -| Logging | `inc/Core/Logger.php`, `Util::logger()` | `Logger` ([utilities](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/utilities.md#logger)) | `Util::logger()->info( 'Imported', [ 'count' => 3 ] )`. Silent unless `WP_DEBUG`. | -| Encryption | `inc/Core/Encryption.php`, `Util::encryption()` | `Encryptor` ([utilities](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/utilities.md#encryptor)) | Define `ACME_CONTENT_FEATURES_ENCRYPTION_KEY` in `wp-config.php` first; without it, calls throw. `Util::encryption()->encrypt( $secret )` / `->decrypt()`. | +| Bootstrap and registration | `acme-content-features.php`, `inc/Autoloader.php`, `inc/Main.php` | `Singleton`, `Loader`, `Registrable`, `Container` ([architecture](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/architecture.md), [contracts](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/contracts.md)) | The entry file defines the `ACME_CONTENT_FEATURES_{FILE,VERSION,PATH,URL}` constants, loads Composer, and boots `Main`. Every loaded class is listed in `Main::CLASSES`. | +| Feature modules | `inc/Modules/.php` | `AbstractModule` ([abstracts](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/abstracts.md#abstractmodule), [modules](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/architecture.md#modules-loaders-that-hold-loaders)) | Each domain's module returns its concrete classes from `get_classes()`. See the [Development guide](../DEVELOPMENT.md#the-module-pattern). | +| Asset registration | `inc/Core/Assets.php`, `src/` | `AssetLoader` ([loaders](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/loaders.md#assetloader)) | Enqueues `main.js`/`main.css` on the frontend, registers `admin` and `editor` handles, registers `block.json` blocks and the Interactivity module. See [Blocks and assets](blocks-and-assets.md). | +| Templates | `inc/Core/Templates.php`, `templates/` | `TemplateLoader` ([loaders](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/loaders.md#templateloader)) | `Util::templates()->get( 'block-templates/example-block-dynamic' )`. A theme overrides a template by copying it to `/acme-content-features/`. | +| Components | `inc/Core/Components.php` | `ComponentLoader` ([loaders](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/loaders.md#componentloader)) | Loader only; no components ship. Put PHP in `src/components//.php` and assets in `src/css/components/` and `src/js/components/`. Resolve it with `Main::get_instance()->get_shared( Components::class )`. | +| Logging | `inc/Core/Logger.php`, `Util::logger()` | `Logger` ([utilities](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/utilities.md#logger)) | `Util::logger()->info( 'Imported', [ 'count' => 3 ] )`. Silent unless `WP_DEBUG`. | +| Encryption | `inc/Core/Encryption.php`, `Util::encryption()` | `Encryptor` ([utilities](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/utilities.md#encryptor)) | Define `ACME_CONTENT_FEATURES_ENCRYPTION_KEY` in `wp-config.php` first; without it, calls throw. `Util::encryption()->encrypt( $secret )` / `->decrypt()`. | | Activation and i18n | `inc/Core/PluginSetup.php` | `Registrable` | Stores `acme_content_features_version` on activation, unschedules the example cron job on deactivation, loads the text domain from `languages/`. | | Helpers | `inc/Helpers/Util.php` | — | Accessors for the shared services above, plus `get_data()` (reads `inc/data/.php`), `is_production()` and `is_mobile()`. | | Quality checks | `tests/`, `phpcs.xml.dist`, `phpstan.neon.dist`, `eslint.config.mjs`, `.stylelintrc.json` | Shared `rtcamp/wp-phpcs`, `rtcamp/wp-phpstan`, `@rtcamp/eslint-config`, `@rtcamp/stylelint-config` | PHPUnit (one test per module), PHPCS, PHPStan level 5, ESLint, Stylelint, Jest. See [Check a change](local-development.md#check-a-change). | -The shared services (`Assets`, `Templates`, `Components`, `Logger`, `Encryption`) implement `Shareable`, so the `Loader` keeps one instance in `Main`'s container. Why that is preferred over singletons is explained in the framework's [sharing vs. singletons](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/architecture.md#sharing-vs-singletons). +The shared services (`Assets`, `Templates`, `Components`, `Logger`, `Encryption`) implement `Shareable`, so the `Loader` keeps one instance in `Main`'s container. Why that is preferred over singletons is explained in the framework's [sharing vs. singletons](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/architecture.md#sharing-vs-singletons). ## Supplied examples @@ -39,8 +39,8 @@ Every set is kept by default. Removing a set at init deletes its module, classes | `settings` | `Modules/Settings/ExampleSettingsPage.php` | `AbstractSettingsPage`: fields, sanitize callbacks, Settings API rendering | **Settings → Acme Content Features**; save the text field and reload. | | `admin` | `Modules/Admin/ExampleAdminPage.php` | `AbstractAdminPage` as a submenu of Tools | **Tools → Features Tools**. | | `roles` | `Modules/Roles/ExampleUserRole.php` | `AbstractUserRole` with versioned capabilities (bump `get_version()` to re-sync) | The role is added on `admin_init`: after loading any admin page, `wp role list` includes `acme_content_content_editor` (**Content Editor**). | -| `cache` | `Modules/Cache/ExampleCache.php` | Framework `Cache`: `remember()` plus group invalidation on `save_post` ([utilities](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/utilities.md#cache)) | No UI; call `get_recent_post_ids()` from your own code or a test. | -| `transients` | `Modules/Transients/ExampleTransients.php` | Framework `Transients` caching a remote request for an hour ([utilities](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/utilities.md#transients)) | No UI; call `get_remote_payload()` from a block, shortcode or REST route. | +| `cache` | `Modules/Cache/ExampleCache.php` | Framework `Cache`: `remember()` plus group invalidation on `save_post` ([utilities](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/utilities.md#cache)) | No UI; call `get_recent_post_ids()` from your own code or a test. | +| `transients` | `Modules/Transients/ExampleTransients.php` | Framework `Transients` caching a remote request for an hour ([utilities](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/utilities.md#transients)) | No UI; call `get_remote_payload()` from a block, shortcode or REST route. | | `test-measure` | `.github/workflows/test-measure.yml` | The CI caller for wp-shared-workflows | Runs on every pull request and on pushes to `master`. | Paths are relative to `inc/`, except the `src/`, `templates/` and `.github/` entries. Each module's test is `tests/php/Test.php`. diff --git a/docs/getting-started.md b/docs/getting-started.md index f807c22..a42d738 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -7,7 +7,7 @@ This guide turns the skeleton into a working plugin: you'll create a personalize - **Git** - **Composer 2** - **PHP 8.2+** (for Composer and the host-side PHPCS/PHPStan checks) -- **Node and npm** matching [`.nvmrc`](../.nvmrc) (Node 22) — use `nvm` or another version manager +- **Node and npm** matching [`.nvmrc`](../.nvmrc) (Node 22.22.2 or later) — use `nvm` or another version manager - **Docker**, running, for the bundled `wp-env` site and the PHP test suite An existing local WordPress installation can replace the `wp-env` site; Docker is still needed for the container-based PHP test command. @@ -32,9 +32,9 @@ composer install npm install ``` -All dependencies install from their public sources: `rtcamp/wp-primitives` and the coding standards from GitHub through Composer, and `@rtcamp/wp-tooling` and the lint configs from their GitHub distribution branches through npm. No token or sibling checkout is needed. +All dependencies install from public registries: `rtcamp/wp-primitives` and the coding standards from Packagist, and `@rtcamp/wp-tooling` and the lint configs from npm. No token or sibling checkout is needed. -`npm install` also installs the commit-message Git hook and runs `npm run sync-ai`, which copies the framework's review rules into `.github/instructions/`. Resolve installation errors before continuing; see [initialization troubleshooting](initialization.md#troubleshooting). +`npm install` also installs two Git hooks (a commit-message check and a pre-commit hook that runs lint-staged) and runs `npm run sync-ai`, which copies the framework's review rules into `.github/instructions/`. Resolve installation errors before continuing; see [initialization troubleshooting](initialization.md#troubleshooting). ## 3. Personalize @@ -78,7 +78,9 @@ npm run wp-env run cli -- wp plugin activate acme-content-features npm run build:dev ``` -The committed [`.wp-env.json`](../.wp-env.json) mounts this directory as a plugin in both the development and test sites. Open [the local site](http://localhost:8888) and [WordPress admin](http://localhost:8888/wp-admin/); a fresh `wp-env` install uses `admin` / `password`. +`wp-env` activates the plugin when it starts, so on a fresh site the activate command reports that the plugin is already active. Keep it for a site where the plugin is not active yet. + +The committed [`.wp-env.json`](../.wp-env.json) mounts this directory as a plugin in the development site; the PHP tests use their own site from [`.wp-env.tests.json`](../.wp-env.tests.json). Open [the local site](http://localhost:8888) and [WordPress admin](http://localhost:8888/wp-admin/); a fresh `wp-env` install uses `admin` / `password`. Confirm the plugin is active under **Plugins**, then check two of the retained examples: diff --git a/docs/internal/maintenance.md b/docs/internal/maintenance.md index 96b79d3..8fd3b02 100644 --- a/docs/internal/maintenance.md +++ b/docs/internal/maintenance.md @@ -104,7 +104,7 @@ git clone https://github.com/rtCamp/wp-tooling.git '/absolute/path/to/wp-tooling git clone https://github.com/rtCamp/wp-primitives.git '/absolute/path/to/wp-primitives' ``` -Check out and record the revision under test. The tooling override needs the source monorepo's `node-packages/` layout, not the `npm/*` distribution branches. Follow the [wp-tooling](https://github.com/rtCamp/wp-tooling) or [wp-primitives contributing guide](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/CONTRIBUTING.md#development-setup) for that package's own setup and checks. +Check out and record the revision under test. The tooling override needs the source monorepo's `node-packages/` layout, not the `npm/*` distribution branches. Follow the [wp-tooling](https://github.com/rtCamp/wp-tooling) or [wp-primitives contributing guide](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/CONTRIBUTING.md#development-setup) for that package's own setup and checks. From the disposable clone, point only the declarations you need at the checkout: diff --git a/docs/local-development.md b/docs/local-development.md index 3fe24c6..44e046a 100644 --- a/docs/local-development.md +++ b/docs/local-development.md @@ -9,6 +9,8 @@ npm run wp-env start npm run wp-env run cli -- wp plugin activate acme-content-features ``` +`wp-env` activates the plugin when it starts; the activate command is for a site where it is not active yet. + | Site | URL | Used for | | --- | --- | --- | | Development | `http://localhost:8888` (`admin` / `password`) | Manual testing | @@ -58,7 +60,7 @@ composer lint # PHPCS (rtCampWP ruleset) composer format # PHPCBF: auto-fix what PHPCS can composer phpstan # PHPStan level 5 -# JavaScript, CSS and package.json. +# JavaScript, CSS and package.json. An initialized plugin has no Jest tests until you add one. npm run test:js npm run lint:js npm run lint:css @@ -78,7 +80,7 @@ npm run test:php:coverage CI runs the same checks through [`.github/workflows/test-measure.yml`](../.github/workflows/test-measure.yml): lint, JS tests, the PHP × WordPress test matrix (edit `php-versions` / `wp-versions` there) and the build, each gated on which files changed. The jobs themselves live in [wp-shared-workflows](https://github.com/rtCamp/wp-shared-workflows). -A commit-message hook installed by `npm install` enforces [Conventional Commits](https://www.conventionalcommits.org/). +The Git hooks installed by `npm install` enforce [Conventional Commits](https://www.conventionalcommits.org/) on commit messages and run lint-staged on staged files before each commit. ## Build for delivery diff --git a/docs/scaffolding.md b/docs/scaffolding.md index 2a75fa5..bcc23e6 100644 --- a/docs/scaffolding.md +++ b/docs/scaffolding.md @@ -109,7 +109,7 @@ Code should read the same whoever, or whatever, writes it: - **The same standards, every run.** Generated code is checked against the gates a developer runs locally: [`phpcs.xml.dist`](../phpcs.xml.dist) (the shared `rtCampWP` ruleset: WPCS, VIP, docs, Slevomat), [`phpstan.neon.dist`](../phpstan.neon.dist) (the `rtcamp/wp-phpstan` level-5 baseline), [`eslint.config.mjs`](../eslint.config.mjs) and [`.stylelintrc.json`](../.stylelintrc.json). - **Verified, not just formatted.** The AI route writes the test first and runs it, then PHPCBF → PHPCS → PHPStan. -- **No drift between developers.** Registration, wiring and lifecycle come from the [`rtcamp/wp-primitives`](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/abstracts.md) abstracts, so the structure is identical whoever generates it; review can focus on the logic. +- **No drift between developers.** Registration, wiring and lifecycle come from the [`rtcamp/wp-primitives`](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/abstracts.md) abstracts, so the structure is identical whoever generates it; review can focus on the logic. The conventions these gates enforce are listed in [AGENTS.md](../AGENTS.md). @@ -117,7 +117,7 @@ The conventions these gates enforce are listed in [AGENTS.md](../AGENTS.md). With the environment running, confirm `wp post-type list` includes `book`, the Book editor shows a Genre panel, and the REST route rejects a non-administrator and creates a draft for an administrator. Commit once the tests and checks pass. -For other kinds, see the installed `list` output and the [upstream scaffold catalogue](https://github.com/rtCamp/wp-tooling/tree/main/node-packages/wp-tooling/scaffolds); each entry's `scaffold.json` lists its inputs and defaults, and the [engine reference](https://github.com/rtCamp/wp-tooling/blob/main/node-packages/wp-tooling/docs/ai-orchestration.md#2-the-engine-surface) documents invocation options. For the methods each generated class can override, see the framework's [abstracts reference](https://github.com/rtCamp/wp-primitives/blob/v1.0.1/docs/abstracts.md). For blocks, see [Blocks and assets](blocks-and-assets.md). To write a class by hand, see the [Development guide](../DEVELOPMENT.md). +For other kinds, see the installed `list` output and the [upstream scaffold catalogue](https://github.com/rtCamp/wp-tooling/tree/main/node-packages/wp-tooling/scaffolds); each entry's `scaffold.json` lists its inputs and defaults, and the [engine reference](https://github.com/rtCamp/wp-tooling/blob/main/node-packages/wp-tooling/docs/ai-orchestration.md#2-the-engine-surface) documents invocation options. For the methods each generated class can override, see the framework's [abstracts reference](https://github.com/rtCamp/wp-primitives/blob/v2.0.0/docs/abstracts.md). For blocks, see [Blocks and assets](blocks-and-assets.md). To write a class by hand, see the [Development guide](../DEVELOPMENT.md). ## Troubleshooting diff --git a/package-lock.json b/package-lock.json index 786ea73..236b64c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,6 +39,9 @@ "lint-staged": "16.1.6", "npm-run-all": "4.1.5", "webpack-remove-empty-scripts": "1.1.1" + }, + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" } }, "node_modules/@ariakit/components": { diff --git a/package.json b/package.json index a023df0..dc232d3 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,9 @@ "bugs": { "url": "https://github.com/rtCamp/plugin-elementary/issues" }, + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + }, "devDependencies": { "@babel/core": "7.29.7", "@eslint-community/eslint-plugin-eslint-comments": "4.7.2",