@axiome-apps/atelier-content
v0.6.2
Published
Déclaration de contenu config-as-code — sections, composants, entités et champs typés, synchronisés vers l'API. Partagé par Échoppe et Prisme.
Downloads
786
Maintainers
Readme
@axiome-apps/atelier-content
Déclaration de contenu config-as-code pour Échoppe. Le développeur décrit ses sections de page et ses components réutilisables en TypeScript ; la CLI sérialise ces définitions vers l'API (registre) et génère les types du front.
C'est un outil build/dev-time : il s'installe en devDependency et ne fait aucun appel runtime.
Installation
pnpm add -D @axiome-apps/atelier-contentConcepts
Trois verbes, trois niveaux :
defineComponent— un atom/molecule : un groupe de champs nommé et réutilisable (button,card…). Non insérable seul dans une page ; s'imbrique dans d'autres définitions.defineSection— un bloc de page : ce que l'éditeur ajoute dans une page et que le front fetch puis boucle (hero,cardGroup…).defineContent— la racine, le seul point que lit la CLI. On y liste les sections ; les components sont collectés automatiquement en suivant les références.
Les directives d'une prose
Une section est un bloc de la page ; une directive est une inflexion du fil, à l'intérieur d'un texte riche. Découper un article en douze sections pour y loger trois encadrés n'a pas d'échelle.
import { defineDirective, directiveRegistry } from '@axiome-apps/atelier-content';
import { parseProse, proseIssues } from '@axiome-apps/atelier-prose';
const content = defineContent({
sections: [article],
directives: [defineDirective('video', { shape: 'leaf', attributes: { id: { required: true } } })],
});
// Le noyau + vos directives, pour valider et rendre.
const registry = directiveRegistry(content);
proseIssues(parseProse(texte), registry);Trois choses valent d'être sues :
- rien n'en va en base. Une directive ne crée pas de table, ne se pousse pas, n'a pas de cache. Elle sert à qui rend, et à lui seul — l'administration affichera donc une directive à vous en brut ;
- déclarer ajoute des garanties, ça n'ouvre rien. Une directive non déclarée traverse déjà,
structurée (
{ name, attributes, children }) et sans garantie de style. La déclarer la fait valider ; - le noyau est fermé —
warning,note,tip,figure,quote,cta,highlight.defineDirectiverefuse de redéfinir l'un d'eux.
Exemple
import { defineComponent, defineSection, defineContent, field as f, link } from '@axiome-apps/atelier-content';
const card = defineComponent('card', {
label: 'Carte',
fields: {
title: f.text({ required: true, maxLength: 80 }),
body: f.richText(), // Markdown
image: f.image(),
cta: link, // component livré : { label, href, newTab }
},
});
const hero = defineSection('hero', {
label: 'Héros',
fields: {
title: f.text({ required: true }),
variant: f.enum({ options: ['clair', 'sombre'] }),
cta: link,
},
});
const cardGroup = defineSection('cardGroup', {
label: 'Groupe de cartes',
fields: {
heading: f.text(),
cards: f.list(card), // répète un component nommé
},
});
export default defineContent({ sections: [hero, cardGroup] });Champs (field, alias f)
Primitifs — text, richText (stocké en Markdown), number ({ integer }),
boolean, date (stocké en ISO 8601, { time }), enum ({ options, multiple }).
Fonctionnels :
image()— un média (UUID), résolu au read.ref({ to })— une référence catalogue (product|collection|category), résolue au read en projection d'entité.component(definition)— imbrique un component nommé à un seul exemplaire (by-reference). Écrire la définition nue (cta: link) fait la même chose, sans pouvoir porter de méta : un component imbriqué n'est obligatoire que déclaré par ce builder.list(component)— répète un component nommé (by-reference).repeater({ fields })— répète un groupe inline anonyme, imbricable à la main (repeaterdansrepeater= menus à sous-niveaux).
Options communes : label, hint, required, default, plus des contraintes par type
(minLength/maxLength, min/max, placeholder, format).
Licence
CeCILL-2.1
