95 lines
5.2 KiB
Markdown
95 lines
5.2 KiB
Markdown
# 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`).
|