Skip to content

[Add] Serve a Gutenberg site in a real WordPress via Playground (#251) - #261

Open
juanmaguitar wants to merge 1 commit into
juanmaguitar/support-gutenberg-as-a-contribution-target-not-ofrom
juanmaguitar/gutenberg-playground-serve
Open

[Add] Serve a Gutenberg site in a real WordPress via Playground (#251)#261
juanmaguitar wants to merge 1 commit into
juanmaguitar/support-gutenberg-as-a-contribution-target-not-ofrom
juanmaguitar/gutenberg-playground-serve

Conversation

@juanmaguitar

Copy link
Copy Markdown
Collaborator

Why

Part of #251, and the piece that makes a Gutenberg site actually usable: seeing a Gutenberg
change running in a real WordPress
. A Core site is served by mounting its build/ as the
WordPress docroot, because that build is a WordPress install. A Gutenberg checkout is a plugin,
not a WordPress — mount it as a docroot and you get an empty site. So the serve strategy has to
follow the site's project type.

Stacked on #255 (project type driving clone/status/build). Part of the Gutenberg feature that
merges atomically — see #255. Do not merge alone.

What changes

  • src/playground-plan.cjs (new, pure) — planPlaygroundLaunch(strategy) returns the
    @wp-playground/cli mount/install options:
    • docroot (Core, unchanged): mount build/ as /wordpress, wordpressInstallMode: install-from-existing-files-if-needed (skip the download; the build already is WordPress).
    • plugin-mount (Gutenberg): leave the install mode at the default so Playground downloads and
      installs a stock WordPress
      , then mount the checkout under
      /wordpress/wp-content/plugins/<slug> and add an activatePlugin step. This mirrors
      @wp-playground/cli's own --auto-mount plugin handling (verified against the CLI source).
  • server-runner.js now takes a JSON serve config (argv[2]) and builds its runCLI options from
    the plan, still adding the debug + SMTP constants from its own environment. The loopback / hide-
    window patches still run before the CLI is required.
  • playground:start resolves the strategy from the site's project type and hands the runner the
    checkout to mount (Gutenberg) or the build dir to serve (Core).

How to test this

Platforms: any. Requires a Gutenberg site (from #255) that has been installed and built.

Automated / mechanism check (no full Gutenberg build needed):

  1. Create a throwaway plugin dir with a single .php plugin header.
  2. Run node src/server-runner.js '{"strategy":"plugin-mount","pluginDir":"<that dir>","pluginSlug":"smoke"}'.
    → It logs Mount … → /wordpress/wp-content/plugins/smoke, installs a stock WordPress, and prints
    SERVER_URL:http://127.0.0.1:9400/; opening it serves WordPress. (This is exactly what I ran to
    verify the plugin-mount path end to end.)

Full flow (a real Gutenberg site):

  1. Create a Gutenberg site; install deps; Run full build (npm run build).
  2. Start the dev server. → It boots a stock WordPress with the Gutenberg checkout active; open
    wp-admin → the block editor reflects the built Gutenberg, not core's bundled version.

What must not have happened:

  • Core is byte-identical. A Core site still mounts its build/ as /wordpress with
    install-from-existing-files-if-needed; the emitted options match the pre-change ones (empty
    mount/additional-blueprint-steps are no-ops). test/ipc-wiring.test.cjs pins the docroot
    config for a no-type site.
  • The loopback bind still precedes the Playground CLI load (test/runner-wiring.test.cjs), so the
    dev site is never briefly served to the LAN.
  • A bad serve config must fail loudly, not hang — the runner exit(1)s and the start request settles
    with an error rather than waiting out the 120s timeout.

What cannot be tested in the suite: the live WASM-PHP boot (network download of stock WP,
minutes on a slow machine). The suite unit-tests the pure planner instead; the boot was verified by
hand as above. Automated: npm run lint clean, 804 tests pass.

Risks and limitations

  • Debug.log panel is empty for a Gutenberg site. The tail reads a host-side
    build/wp-content/debug.log; a plugin-mounted (VFS) WordPress does not write there. It degrades
    quietly — the watcher no-ops on the missing file/dir, no crash — rather than failing. A
    Gutenberg-aware debug path is a follow-up.
  • Stock WordPress version is Playground's default (latest stable); Gutenberg trunk supports it, but
    pinning a WP version is a future option if a mismatch ever surfaces.

Related

Part of #251. Stacked on #255.


Review outcome (required — see AGENTS.md)

0 [fix here] · 1 [follow-up]. Ran the review in
.github/instructions/code-review.instructions.md; judgement pass in a fresh subagent. Lint clean,
804 tests pass.

  • 🔵 Cross-platform/perf · [follow-up] — the debug.log tail path (build/wp-content/debug.log) is
    Core-shaped; a plugin-mounted Gutenberg WordPress doesn't write there. Verified it degrades
    gracefully (watcher no-ops on the missing file/dir), so the panel is inert rather than broken.
    Documented above; a Gutenberg-aware debug path rides with the later copy/UX work.
  • Verified clean: the loopback-before-CLI security invariant still holds after the refactor; the
    Core docroot option set is behaviorally identical (empty mount/additional-blueprint-steps are
    no-ops, path.resolve drop is harmless since the path is already absolute); failure paths are
    loud (runner exit(1) settles the start request, no silent hang); untrusted-input handling is
    fine (JSON built from a registered path, shell:false); VFS guest paths use POSIX templates while
    host paths use path.join; tests pin both forks and would fail on a regression.
Implementation notes

The plugin-mount recipe is not invented — it's the exact shape @wp-playground/cli builds for a
plugin passed to --auto-mount: a mount of the host dir to /wordpress/wp-content/plugins/<slug>
plus an { step: 'activatePlugin', pluginPath } in additional-blueprint-steps, with the default
(download-and-install) WordPress. Keeping the planner pure (src/playground-plan.cjs) is what lets
the fork be unit-tested without booting WASM PHP; server-runner.js merges the plan with the
env-derived debug/SMTP constants and spawns.

A Core site is served by mounting its build/ as the WordPress docroot, because
that build IS a WordPress install. A Gutenberg checkout is a plugin, not a
WordPress, so the same path serves an empty site. Make the serve strategy follow
the site's project type.

- src/playground-plan.cjs: a pure planPlaygroundLaunch(strategy) that returns the
  @wp-playground/cli mount/install options — 'docroot' mounts build/ as
  /wordpress and skips the download (Core, unchanged), 'plugin-mount' lets
  Playground install a stock WordPress and mounts the checkout as an active
  plugin under wp-content/plugins/<slug> (Gutenberg). This mirrors the CLI's own
  --auto-mount handling (a `mount` plus an `activatePlugin` step).
- server-runner.js now takes a JSON serve config and builds its runCLI options
  from the plan, keeping the debug + SMTP constants it adds from its own env.
- playground:start resolves the strategy from the site's type and hands the
  runner the checkout to mount (Gutenberg) or the build dir to serve (Core).

Verified the plugin-mount path end to end against a throwaway plugin: Playground
downloads a stock WordPress, mounts the plugin, and reports a URL that serves
WordPress. Core's docroot path is byte-identical.

Known gap: the debug.log tail reads a host-side build/wp-content/debug.log, which
a plugin-mounted (VFS) WordPress does not write — so the debug-log panel is empty
for a Gutenberg site. It degrades quietly (the tail no-ops on a missing file)
rather than failing; a Gutenberg-aware debug path is a follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gutenberg-contributions Support Gutenberg as a contribution target (#251)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant