wp·reactor

Des blocs Gutenberg générés, pas écrits à la main

WP Reactor produit le bloc WordPress, le renderer React et le typage TypeScript à partir d'un seul schéma. Un fichier à maintenir au lieu de sept.

apps/webapp/src/blocks/schemas/spec-list.json
{
  "slug": "spec-list",
  "title": "Spec List",
  "attributes": {
    "heading": { "ui": "TextInput", "label": "Titre" },
    "columns": {
      "ui": "Select",
      "default": "two",
      "options": [
        { "label": "Une colonne", "value": "one" },
        { "label": "Deux colonnes", "value": "two" }
      ]
    },
    "items": {
      "ui": "ArrayRepeater",
      "fields": [
        { "key": "term", "ui": "TextInput" },
        { "key": "description", "ui": "Textarea" }
      ]
    }
  }
}

Du schéma au bloc rendu, en une commande

Trois étapes. Une seule est manuelle : le renderer, le seul fichier que tu écris.

  1. 01Schéma

    Tu décris le bloc

    Un JSON déclare les attributs et leur contrôle éditeur. La clé ui est la seule source de vérité : elle dérive à la fois le type WordPress et le type TypeScript.

    {
      "slug": "spec-list",
      "title": "Spec List",
      "attributes": {
        "heading": { "ui": "TextInput", "label": "Titre" },
        "columns": {
          "ui": "Select",
          "default": "two",
          "options": [
            { "label": "Une colonne", "value": "one" },
            { "label": "Deux colonnes", "value": "two" }
          ]
        },
        "items": {
          "ui": "ArrayRepeater",
          "fields": [
            { "key": "term", "ui": "TextInput" },
            { "key": "description", "ui": "Textarea" }
          ]
        }
      }
    }
  2. 02Génération

    La commande écrit les sept fichiers

    Six côté thème, un côté webapp. Le registre de blocs et l'import de l'éditeur sont câblés au passage, sans édition manuelle.

    $ pnpm gen:block apps/webapp/src/blocks/schemas/spec-list.json
    
    ✓ Generated block: wpreactorapp/spec-list
      - theme-wp-reactor-app/src/blocks/spec-list/block.json
      - theme-wp-reactor-app/src/blocks/spec-list/edit.tsx
      - theme-wp-reactor-app/src/blocks/spec-list/index.tsx
      - theme-wp-reactor-app/src/blocks/spec-list/save.tsx
      - theme-wp-reactor-app/src/blocks/spec-list/render.php
      - theme-wp-reactor-app/src/blocks/spec-list/editor.scss
      - apps/webapp/src/blocks/ui/SpecListBlock.tsx
      - registry wired (webapp, lazy chunk)
      - editor import wired (theme)
  3. 03Renderer

    Tu écris le rendu, et rien d'autre

    Le seul fichier laissé en placeholder. Les attributs arrivent déjà normalisés : pas de JSON.parse, pas de conversion de tableau.

    export function SpecListBlock({ attributes }: SpecListBlockProps) {
      const { heading, columns, items } = attributes ?? {};
    
      return (
        <section className="border-b border-border bg-canvas">
          <h2 className="font-semibold text-3xl tracking-tight">{heading}</h2>
          <dl className={columns === "one" ? "grid" : "grid sm:grid-cols-2"}>
            {(items ?? []).map((entry, index) => (
              <div key={entry.__wpReactorItemId ?? index}>
                <dt className="font-mono text-accent-text">{entry.term}</dt>
                <dd className="text-muted">{entry.description}</dd>
              </div>
            ))}
          </dl>
        </section>
      );
    }

L'éditeur affiche le vrai front, pas une approximation

Dans Gutenberg, le bloc est rendu par un iframe qui charge la route /preview de la webapp. Le thème parent et le plugin headless fournis par WP Reactor portent le pont postMessage.

Éditeur Gutenberg

iframe → /preview

<HeroBlock attributes={…} />

[ Éditer ]

même fichier — apps/webapp/src/blocks/ui/HeroBlock.tsx

Site public

<HeroBlock attributes={…} />

Aucun markup dupliqué
L'aperçu n'est pas une seconde implémentation en PHP ou en JS d'éditeur : c'est le composant React du site, appelé avec les attributs en cours d'édition.
Fourni, pas à câbler
Le thème parent wp-reactor-base et le plugin wp-reactor-headless portent le pont. Le thème enfant ne déclare que ses propres blocs.
Pas d'écart possible
Une divergence entre l'aperçu et la production ne peut pas apparaître, puisqu'il n'existe qu'un seul rendu.

Les contrôles disponibles dans un schéma

Chaque valeur de ui produit un contrôle Gutenberg et un type TypeScript. La liste est le contrat : déclarer un type à la main n'est jamais nécessaire.

TextInput
Texte court — titre, libellé, badge. Type TS : string.
RichText
Éditeur riche : gras, italique, liens. Type TS : string, sérialisé en HTML.
Textarea
Texte long sans mise en forme, hauteur réglable via rows. Type TS : string.
Select
Sélecteur de variante. Type TS : union littérale des valeurs déclarées.
Toggle
Interrupteur vrai/faux. Type TS : boolean.
ArrayRepeater
Items répétables et imbricables. Type TS : Array du shape déclaré dans fields.
Image
Média WordPress. Le renderer reçoit alt et les tailles générées.
Cta
Couple libellé + URL, pour un bouton ou un lien.

Générer ton premier bloc

La commande sans argument est interactive : elle liste les schémas à créer et ceux à régénérer.