Aller au contenu

Système d'espaces réservés

Le système d’espaces réservés permet aux lecteurs de personnaliser les adresses IP, les ASN et d’autres valeurs spécifiques au déploiement dans toute la documentation. Les auteurs écrivent des jetons dans leur Markdown ; le navigateur les remplace par les valeurs fournies par l’utilisateur au moment de l’exécution.

Les jetons suivent le modèle d’expression régulière :

x([A-Z][A-Z0-9_]+)x

Un jeton commence et se termine par un x minuscule et contient un identifiant en majuscules. Par exemple, xCUSTOMER_ASNx fait référence à l’espace réservé CUSTOMER_ASN.

L’expression régulière est définie dans src/scripts/placeholder-dom.ts :

const PH_REGEX = /x([A-Z][A-Z0-9_]+)x/g;

Tous les espaces réservés sont déclarés dans src/data/placeholders.json. Chaque entrée a cette structure :

{
"CUSTOMER_ASN": {
"type": "text",
"default": "64496",
"description": "Your public ASN (registered with ARIN/RIR)"
}
}
ChampRequisDescription
typeoui"text" pour une saisie libre, "dropdown" pour les menus de sélection
defaultouiValeur initiale affichée avant que le lecteur ne modifie quoi que ce soit
descriptionouiLibellé affiché dans le formulaire
optionsuniquement pour dropdownTableau des valeurs autorisées

src/lib/placeholder-store.ts gère l’ensemble de l’état des espaces réservés.

Les valeurs sont persistées dans localStorage sous la clé f5xc-placeholders. Le store expose quatre fonctions :

FonctionObjectif
getDefaults()Retourne une correspondance de chaque clé d’espace réservé vers sa valeur default depuis le JSON
loadValues()Lit depuis localStorage, se rabat sur getDefaults()
saveValues(values)Écrit la correspondance courante dans localStorage
clearValues()Supprime l’entrée localStorage

FIELD_GROUPS organise les clés des espaces réservés en sections étiquetées pour l’interface du formulaire :

export const FIELD_GROUPS: FieldGroup[] = [
{ label: 'Data Center & Scrubbing Centers', keys: ['DC_NAME', 'CENTER_1', 'CENTER_2'] },
{ label: 'Protected Prefixes', keys: ['PROTECTED_CIDR_V4', 'PROTECTED_NET_V4', ...] },
{ label: 'BGP', keys: ['CUSTOMER_ASN', 'F5_XC_ASN', 'BGP_PASSWORD'] },
// ... more groups
];

Certaines valeurs sont dérivées de la saisie utilisateur plutôt que saisies directement. getComputedValues() les calcule à partir de tables de correspondance :

const cidrToMask: Record<string, string> = {
'/24 (256 IPs)': '255.255.255.0',
'/23 (512 IPs)': '255.255.254.0',
// ...
};

Deux espaces réservés calculés sont produits :

Clé calculéeDérivée deExemple
PROTECTED_MASK_V4PROTECTED_CIDR_V4 via la table cidrToMask255.255.255.0
PROTECTED_PREFIX_V4PROTECTED_NET_V4 + PROTECTED_CIDR_V4 via cidrToShort192.0.2.0/24

getAllValues() fusionne les valeurs saisies par l’utilisateur avec les valeurs calculées, fournissant une correspondance complète pour la substitution.

emitChange() envoie un CustomEvent placeholder-change sur document avec la correspondance complète des valeurs dans detail :

export function emitChange(values: Record<string, string>) {
document.dispatchEvent(
new CustomEvent('placeholder-change', { detail: getAllValues(values) }),
);
}

Cet événement pilote à la fois les mises à jour des spans du DOM et le re-rendu de Mermaid.

src/components/PlaceholderForm.tsx fournit l’interface d’édition.

  • État : useState initialisé à partir de loadValues()
  • Au montage : useEffect appelle emitChange() pour déclencher la substitution initiale du DOM
  • handleChange : Met à jour l’état React, appelle saveValues() et emitChange()
  • handleReset : Appelle clearValues(), réinitialise l’état à getDefaults(), émet le changement
  • Rendu : Itère sur FIELD_GROUPS, affichant un <fieldset> par groupe. Chaque clé obtient soit un <input> (type text) soit un <select> (type dropdown)
  • Mise en page : Le formulaire est encapsulé dans un élément <details>, replié par défaut

src/components/PlaceholderFormWrapper.astro connecte le composant React à la page Astro :

<PlaceholderForm client:only="react" />
<script>
import '../scripts/placeholder-dom.ts';
</script>

client:only="react" indique à Astro d’hydrater le composant uniquement côté client (pas de SSR). La balise <script> importe le parcoureur de DOM pour qu’il s’exécute sur chaque page incluant ce wrapper.

Le wrapper injecte également du CSS global pour la mise en forme du formulaire (.ph-form-wrapper, .ph-grid, .ph-value, etc.).

src/scripts/placeholder-dom.ts gère le remplacement des jetons côté client.

Au chargement de la page, init() s’exécute :

  1. Sélectionne .sl-markdown-content comme racine (se rabat sur document.body)
  2. Appelle walkTextNodes(root, values) qui utilise document.createTreeWalker avec NodeFilter.SHOW_TEXT
  3. Pour chaque nœud texte correspondant à l’expression régulière du jeton, le découpe en un fragment de document composé de nœuds texte simples et d’éléments <span data-ph="KEY" class="ph-value">
  4. Remplace le nœud texte original par le fragment

Après le parcours, le DOM contient des spans avec des attributs data-ph au lieu des jetons bruts.

Lorsque le formulaire émet un événement placeholder-change, updateSpans() s’exécute :

document.querySelectorAll<HTMLSpanElement>('span[data-ph]').forEach((span) => {
const name = span.getAttribute('data-ph')!;
if (values[name] !== undefined) {
span.textContent = values[name];
}
});

Cela évite de re-parcourir l’arbre — le contenu textuel des spans est directement mis à jour.

Le script enregistre deux écouteurs :

ÉvénementGestionnaireObjectif
placeholder-changehandleChangeMet à jour les spans et re-rend les diagrammes Mermaid
astro:page-loadinitRe-parcourt le DOM après une navigation côté client Astro

Guide pratique : Ajouter un nouvel espace réservé

Section intitulée « Guide pratique : Ajouter un nouvel espace réservé »
  1. Ajoutez l’entrée JSON dans src/data/placeholders.json :

    "MY_NEW_VALUE": {
    "type": "text",
    "default": "example",
    "description": "Description shown in the form"
    }
  2. Ajoutez la clé à un groupe de champs dans src/lib/placeholder-store.ts. Soit ajoutez-la au tableau keys d’un groupe existant, soit créez un nouveau groupe dans FIELD_GROUPS.

  3. Utilisez le jeton dans le contenu : Écrivez xMY_NEW_VALUEx dans n’importe quel fichier .mdx. Le parcoureur de DOM le remplacera au moment de l’exécution.

Les valeurs calculées sont dérivées d’autres espaces réservés. Pour en ajouter une :

  1. Ajoutez une table de correspondance (si nécessaire) dans src/lib/placeholder-store.ts, en suivant le modèle de cidrToMask.

  2. Étendez getComputedValues() pour inclure la nouvelle clé dérivée :

    export function getComputedValues(values: Record<string, string>): Record<string, string> {
    // ... existing logic
    return {
    PROTECTED_MASK_V4: mask,
    PROTECTED_PREFIX_V4: `${net}${short}`,
    MY_COMPUTED: derivedValue, // add here
    };
    }
  3. Utilisez xMY_COMPUTEDx dans le contenu comme n’importe quel autre jeton. Les valeurs calculées n’ont pas besoin d’une entrée dans placeholders.json ni d’un groupe de champs — elles sont invisibles pour le formulaire.