Improve wp acorn compability

This commit is contained in:
Jan-Niclas Loosen
2026-06-29 19:54:12 +02:00
parent 7f3fd36186
commit 102abe7927
28 changed files with 4090 additions and 1980 deletions
+94
View File
@@ -0,0 +1,94 @@
# CLAUDE.md
Guidance for working in this repository.
## What this is
A **starter WordPress plugin** built on the [Roots Acorn](https://roots.io/acorn/)
framework (Laravel components inside WordPress) with **Livewire 4** for reactive
front-end components. It is a scaffold/template designed to **coexist with other
Acorn-based plugins** in the same WordPress install without booting the framework
more than once.
- `roots/acorn: ^6.0`, `livewire/livewire: ^4.1`
- Requires PHP 8.4, WordPress 5.2+
- PSR-4: `AcornPlugin\``app/`, `AcornPlugin\Tests\``tests/`
- Acorn is bundled (in `vendor/`); the plugin `wp_die`s if `Roots\Acorn\Application`
is somehow absent.
## The single-Acorn contract (read this before touching `bootstrap.php`)
PHP classes and the Acorn container are **global to a request**. If two Acorn
plugins each call `Application::configure()->boot()`, the second clobbers the
first. This plugin avoids that with a cooperative **boot election** that every
Acorn plugin in the install must follow:
1. **One booter per request, elected via a shared action.** On `after_setup_theme`
priority 0, a plugin boots Acorn only if `did_action('acorn/booted')` is false,
then fires `do_action('acorn/booted')`; otherwise it stands down. This lives in
`app/Acorn/BootCoordinator.php` (`BootCoordinator::BOOTED_ACTION = 'acorn/booted'`).
`did_action()` is used deliberately — it is a **read-only** probe, unlike
`Application::getInstance()`, which force-creates and installs an empty container.
2. **Providers are auto-discovered, never hand-registered.** Each plugin declares
its provider in `composer.json` under `extra.acorn.providers`. Whichever plugin
wins the election scans **all** active plugins' manifests and registers every
provider — so a plugin's provider boots even when a *sibling* booted Acorn.
3. **Livewire assets are emitted once, by the boot owner.** Only the elected booter
calls `LivewireAssets::registerOnce()` (`@livewireStyles` on `wp_head`,
`@livewireScripts` on `wp_footer`). Livewire 4 self-initialises — never add
`Livewire.start()` or a hand-rolled ESM `<script import>` per plugin.
**Rules for every Acorn plugin sharing this install (including siblings like
`siteminder-integration`):**
- Use the **exact action name `acorn/booted`** — it is the cross-plugin contract.
- Give the plugin its **own PSR-4 namespace** (do *not* reuse `AcornPlugin\`), or
`BootCoordinator`/`LivewireAssets`/provider classes collide by FQCN (first
autoloader wins).
- Namespace your views (`loadViewsFrom($path, 'your-plugin')`) and Livewire
components (`Livewire::addNamespace('your-plugin', …)`) so they don't collide in
the shared container.
- Keep all co-installed Acorn plugins on the **same Acorn major version** — they
share one bundled copy at runtime (first loaded wins).
**Caveat:** the election only coordinates plugins that adopt this contract. A Sage
theme (or anything else) that boots Acorn will *not* fire `acorn/booted`, so the
plugins would boot a second container on top of it. If a Sage theme is ever added,
make the **theme** the sole booter and let plugins rely on `extra.acorn.providers`
auto-discovery alone — drop the `configure()->boot()` from the plugins.
## Layout
- `index.php` — plugin header; includes `bootstrap.php`.
- `bootstrap.php` — runs the boot election (above); on win, boots Acorn with
`->withRouting(wordpress: true)` and emits Livewire assets once. **No**
`withProviders()` — auto-discovery handles providers.
- `app/Acorn/BootCoordinator.php` — the election seam (unit-tested).
- `app/Acorn/LivewireAssets.php` — once-only Livewire asset emission.
- `app/Providers/PluginServiceProvider.php` — registers namespaced views, the
`acorn-starter` Livewire namespace, and the `[acorn_hello]` shortcode. Extends
`Illuminate\Support\ServiceProvider` (Acorn's `ServiceProvider` is `final` in v6).
- `resources/views/``hello-world.blade.php` (mounts `<livewire:acorn-starter::say-hello />`),
`components/say-hello.blade.php` (anonymous single-file Livewire component).
- `tests/` — PHPUnit + Brain\Monkey unit tests (`tests/Acorn/BootCoordinatorTest.php`).
- `storage/framework/` — compiled views / cache / sessions (gitignored runtime dirs).
- `.env``APP_KEY` (generate with `wp acorn key:generate`).
There is **no plugin-level `config/`**: a plugin shouldn't own the framework's
global config, so the elected booter uses Acorn's defaults. Add config only if this
plugin is intentionally the sole booter.
## Conventions
- New classes go under `app/` in the `AcornPlugin\` namespace.
- Reference views by their namespace: `view('acorn-starter::…')`.
- Naming: kebab (`acorn-starter`) for view/Livewire namespaces; the public shortcode
stays `acorn_hello` (snake) as a published API.
- Match the test style in `tests/` (Brain\Monkey, AAA, `test_` snake_case methods).
## Commands
- `composer install` — install deps (runs Acorn's `post-autoload-dump`, which
rebuilds the package-discovery manifest; rerun after editing `extra.acorn.providers`).
- `composer test` — PHPUnit unit suite.
- `composer lint` — phpcs (PSR-12 + WordPress security/i18n sniffs).
- `wp acorn …` — Acorn WP-CLI (e.g. `wp acorn key:generate`, `wp acorn package:discover`).