Aller au contenu
duxt

Hooks

L’unique hook de build que duxt émet, ce qu’il vous remet et pourquoi il n’y en a qu’un.

duxt émet un hook. C’est un hook de build Nuxt, il se déclenche une fois par build et vous remet chaque page du site sous forme d’enregistrements de recherche plats.

import { defineNuxtModule } from '@nuxt/kit';
import type { DuxtSearchRecord } from '@kirchdev/duxt/search-records';

export default defineNuxtModule({
  setup(_options, nuxt) {
    nuxt.hook('duxt:search:records', async (records) => {
      await upload(records);
    });
  }
});

Un exemple complet — le schéma de la collection, l’upsert, la suppression de ce qui a disparu et la clé qui ne doit jamais atteindre une page — se trouve dans Envoyer vos pages vers Typesense.

duxt:search:records

(records: readonly DuxtSearchRecord[]) => void | Promise<void>

Quand il se déclenche. Après que Content a analysé chaque source, chaque version, chaque langue et chaque section générée — y compris les dépôts distants qu’il a téléchargés — et avant que Nitro ne prérende ou n’empaquette quoi que ce soit. Un listener peut donc encore écrire un fichier dans public/ et le voir arriver dans la sortie.

Ce qu’est un enregistrement. Une page, plus un par titre qu’elle porte.

ChampTypeCe qu’il contient
idstringL’identité de l’enregistrement, et une clé primaire valide partout
urlstringOù atterrit le lecteur — segment de langue et ancre compris
titlestringLe titre que couvre l’enregistrement, ou le titre de la page pour le premier
contentstringLes mots sous ce titre, sans balisage et avec les espaces normalisés
sourcestringDe quelle source il vient, en segments d’URL sans la version
versionstringL’étiquette de version, là où la source en a — absente sinon
localestringLa langue dans laquelle le contenu est écrit

id est une empreinte hexadécimale plutôt que l’URL, parce que Meilisearch n’accepte que A-Za-z0-9_- comme identifiant de document. Elle est dérivée de url, donc identique sur toutes les machines et stable tant que la page reste où elle est — ce qui permet à un fournisseur de faire un upsert plutôt qu’une reconstruction.

source est une identité, pas une adresse : / pour la documentation servie à la racine, /harbour pour un second dépôt, /harbour/api pour une section générée à l’intérieur. La version en est délibérément absente, afin qu’une facette bâtie sur source survive à une publication et que version, à côté, soit ce qui restreint à une seule.

locale est la langue du contenu, pas la langue d’interface dans laquelle on lit. Un site dont les sources ne sont pas traduites obtient un jeu d’enregistrements même s’il sert sept langues : le même paragraphe indexé sept fois, c’est une réponse répétée.

Ce qu’il ne porte délibérément pas

  • Aucun extrait. Un extrait est une décision sur une liste de résultats, et la liste de résultats est la vôtre.
  • Aucun nom de collection. C’est la comptabilité interne de l’index de duxt, et un document indexé là-dessus casse le jour où une source gagne une langue.
  • Aucun libellé d’affichage. label, name et la mention de version sont de la prose pour un lecteur, traduite par langue. Un enregistrement n’est lu par aucun lecteur.

Ce qui reste hors de l’index

Une page qui a écrit search: false ne produit aucun enregistrement — le même retrait que respectent déjà la boîte de recherche, l’outil MCP search_docs et les deux index llms. Il en va de même d’une source traduite dans une langue que duxt.locales ne sert pas, car l’indexer reviendrait à proposer au lecteur une URL qui renvoie 404.

Là où un site sert deux régions d’une même langue sur un seul arbre — pt-PT et pt-BR lisant le même dossier pt/ —, les enregistrements visent la première. Les deux URL portent les mêmes mots : les indexer toutes les deux doublerait l’index pour dire deux fois la même chose. Un fournisseur qui veut envoyer un lecteur dans sa propre région remplace le segment, ce que duxt ne peut pas faire puisqu’il ignore qui cherche.

Et quand personne n’écoute, duxt ne construit aucune charge : un site sans fournisseur ne paie rien pour l’existence du hook.

Pourquoi il n’y en a qu’un

Une extension duxt distribuable est une couche Nuxt, pas un objet plugin :

export default defineNuxtConfig({
  extends: ['@kirchdev/duxt', 'duxt-typesense']
});

Cela porte déjà des composants, des valeurs par défaut d’app.config, des collections content.config.ts, des routes serveur, des outils MCP et un module à soi, dans un paquet avec sa propre version. Une API duxt.plugins réimplémenterait la résolution de couches à l’intérieur d’une couche — et un registre dans lequel un plugin écrit à l’import se heurte au mur qui a façonné sectionTypes : content.config.ts et le module de duxt sont chargés par des chargeurs différents, donc un registre mutable qu’ils rempliraient tous les deux ferait deux registres. Un hook de build Nuxt n’a pas ce problème, car seule la moitié module le voit.

La surface reste donc petite à dessein, et chacune des autres entrées demeure la bonne réponse à sa propre question :

Vous voulez changerUtilisez
Une couleur, une police, un espacementUn token CSS — Surcharger le thème
Ce qu’un composant dessineLe masquer, ou un slot là où il y en a un
Ce que le site dit de lui-mêmeLa clé duxt d’app.config — Configuration
Un nouveau type de section généréesectionTypes — Sources
Ce qu’une page dessine autour d’elleLe frontmatter — Frontmatter
Où le texte du site est indexéCe hook
Cette page vous a-t-elle été utile ?