Une extension qui gère cinq types de contenu différents, chacun avec son propre bloc d’affichage, ne justifie pas cinq extensions séparées à activer indépendamment. La bonne échelle reste une seule extension, avec une arborescence organisée pour que l’ajout d’un sixième bloc, dans six mois, ne demande pas de réinventer la structure.
Arborescence de référence
mon-extension/
├── mon-extension.php
├── package.json
├── build/
│ ├── fiche-produit/
│ │ ├── index.js
│ │ ├── index.asset.php
│ │ └── style-index.css
│ └── avis-client/
│ ├── index.js
│ ├── index.asset.php
│ └── style-index.css
└── src/
├── fiche-produit/
│ ├── block.json
│ ├── edit.js
│ ├── save.js
│ ├── index.js
│ ├── style.scss
│ └── editor.scss
└── avis-client/
├── block.json
├── edit.js
├── save.js
├── index.js
├── style.scss
└── editor.scss
Chaque bloc vit dans son propre sous-dossier de src/, avec sa propre déclaration block.json, ses propres fichiers edit/save, et ses propres feuilles de style. Cette symétrie entre src/ et build/ rend triviale l’identification de l’origine d’un problème de compilation.
Un build unique avec @wordpress/scripts
Depuis les versions récentes de @wordpress/scripts, la commande build détecte automatiquement plusieurs points d’entrée si elle reçoit un motif de chemin, sans configuration Webpack manuelle :
{
"scripts": {
"build": "wp-scripts build src/*/index.js",
"start": "wp-scripts start src/*/index.js"
}
}
Cette seule ligne suffit à compiler tous les blocs présents dans src/, chacun dans son propre sous-dossier de sortie sous build/, avec son fichier index.asset.php listant les dépendances et la version de hachage — indispensable pour un wp_enqueue_script qui gère correctement le cache navigateur.
Enregistrement PHP : parcourir plutôt que lister

Plutôt que d’appeler register_block_type une fois par bloc, un parcours du dossier build/ évite d’oublier une déclaration lors de l’ajout d’un nouveau bloc :
<?php
add_action( 'init', function () {
foreach ( glob( __DIR__ . '/build/*/block.json' ) as $chemin ) {
register_block_type( dirname( $chemin ) );
}
} );
register_block_type accepte directement un chemin de dossier contenant un block.json et se charge de lire le fichier, d’enregistrer les scripts et styles associés selon leurs clés (editorScript, script, style) et de les relier au bloc. Il faut s’assurer que le block.json source est bien copié dans build/ — @wordpress/scripts s’en charge automatiquement depuis les versions qui le supportent nativement.
Nommage : éviter les collisions
Chaque bloc doit porter un nom préfixé par l’espace de noms de l’extension, jamais un nom générique :
mon-extension/fiche-produit, jamais simplementfiche-produit.- Un préfixe identique sur tous les blocs de l’extension facilite le filtrage dans l’inserteur de blocs et dans les journaux de débogage.
- Le nom du dossier source peut rester court (
fiche-produit), seul le nom déclaré dansblock.jsonporte l’espace de noms complet.
Code partagé entre blocs
Quand plusieurs blocs partagent une fonction utilitaire ou un composant d’interface, un dossier src/shared/ (non compilé en point d’entrée direct) évite la duplication, à condition que chaque index.js l’importe explicitement plutôt que de dupliquer le code.
src/
├── shared/
│ └── formater-prix.js
├── fiche-produit/
│ └── edit.js // import { formaterPrix } from '../shared/formater-prix';
└── avis-client/
└── edit.js // import { formaterPrix } from '../shared/formater-prix';
Sur une extension qui dépasse cinq ou six blocs, le temps gagné par cette organisation en amont dépasse largement le temps investi à la mettre en place dès le premier bloc.
Où cette approche montre ses limites
Cette structure convient à une extension unique avec plusieurs blocs liés fonctionnellement. Si le projet grandit au point de justifier plusieurs équipes travaillant sur des blocs totalement indépendants, avec des cycles de publication distincts, une architecture en plusieurs paquets séparés (un monorepo avec des espaces de travail npm, par exemple) devient plus pertinente — un sujet qui dépasse le cadre d’une extension unique.