5.2 KiB
CLAUDE.md
Guidance for working in this repository.
What this is
A starter WordPress plugin built on the Roots 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 pluginwp_dies ifRoots\Acorn\Applicationis 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:
- One booter per request, elected via a shared action. On
after_setup_themepriority 0, a plugin boots Acorn only ifdid_action('acorn/booted')is false, then firesdo_action('acorn/booted'); otherwise it stands down. This lives inapp/Acorn/BootCoordinator.php(BootCoordinator::BOOTED_ACTION = 'acorn/booted').did_action()is used deliberately — it is a read-only probe, unlikeApplication::getInstance(), which force-creates and installs an empty container. - Providers are auto-discovered, never hand-registered. Each plugin declares
its provider in
composer.jsonunderextra.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. - Livewire assets are emitted once, by the boot owner. Only the elected booter
calls
LivewireAssets::registerOnce()(@livewireStylesonwp_head,@livewireScriptsonwp_footer). Livewire 4 self-initialises — never addLivewire.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\), orBootCoordinator/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; includesbootstrap.php.bootstrap.php— runs the boot election (above); on win, boots Acorn with->withRouting(wordpress: true)and emits Livewire assets once. NowithProviders()— 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, theacorn-starterLivewire namespace, and the[acorn_hello]shortcode. ExtendsIlluminate\Support\ServiceProvider(Acorn'sServiceProviderisfinalin 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 withwp 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 theAcornPlugin\namespace. - Reference views by their namespace:
view('acorn-starter::…'). - Naming: kebab (
acorn-starter) for view/Livewire namespaces; the public shortcode staysacorn_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'spost-autoload-dump, which rebuilds the package-discovery manifest; rerun after editingextra.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).