Documentazione · src/engine
Il motore
Sette file alla radice e quattro sottocartelle. Qui il dato diventa qualcosa che si può disegnare, e succede sul server — prima che React esista.
Un blocco è un dato con quattro chiavi lette per nome
Di tutto quello che un blocco contiene, il motore ne guarda quattro cose. blockType dice quale resolver lo tratta. viewMode nomina la forma. slot dice in quale apertura del genitore va proiettato. content tiene i figli. Tutto il resto — il testo, l’immagine, l’href, i campi che quel tipo di blocco ha — appartiene al blocco e il motore non lo interpreta: lo passa.
È una scelta con una conseguenza precisa: aggiungere un campo a un blocco non richiede di toccare il motore. Il campo finisce in data e arriva al componente. Il motore resta ignorante del dominio, che è la ragione per cui non cresce quando cresce il progetto.
il tipo UimsBlock: le quattro chiavi note più l’apertura al resto
La forma di un blocco
type UimsBlock = {
blockType: string
viewMode?: string
slot?: string
className?: string
content?: UimsBlock[]
uiState?: Record<string, unknown>
[field: string]: unknown // i campi propri del tipo di blocco
}Il contesto: una mappa da tipo di blocco a resolver
createUIMSContext costruisce l’oggetto che il progetto tiene in uimsContext.ts e usa in ogni pagina: UIMS.resolve(block). Dentro non c’è nient’altro che una mappa da blockType alla funzione che lo tratta, più il default per tutti i tipi che non ne hanno uno dedicato.
Un resolver può restituire null, e questo è il dettaglio che evita una classe intera di crash: un sectionRef che punta a una sezione cancellata risolve a niente. Non è un errore, è la risposta giusta — la sezione non c’è. Il renderer salta il descrittore vuoto e la pagina esce lo stesso.
Il blocco in ingresso resta any, e non per pigrizia: il tipo di un blocco dipende dalla configurazione del progetto, che il pacchetto non conosce. La tipizzazione forte sta dall’altra parte, sul descrittore che esce.
costruisce il contesto: registro dei resolver, default, e resolve
la porta pubblica dell’area
Gli store, tipizzati una volta sola
db.viewmodes.json e db.styles.json sono le due sorgenti di forma, e hanno un tipo: entrano nel motore come ViewModeDef[] e StyleDef[], non come liste di qualcosa. Un cast al momento della chiamata sembrerebbe una precauzione ed è una rinuncia ripetuta: chi lo scrive dichiara di non sapere cosa ha in mano.
store.ts dichiara ViewModeDef e StyleDef in un punto solo, e ogni consumatore parte da lì. Il tipo descrive lo STORE, non la forma a runtime: quello che esce dal registry è già fuso con l’override del tema e normalizzato per il renderer, ed è un’altra cosa — descritta in engine/types.ts.
ViewModeDef e StyleDef: cosa contengono i due store
I tipi degli slot stanno dove servono
I quattro tipi che descrivono gli slot — la definizione, l’istanza di un figlio, lo slot risolto e la mappa di tutti — vivevano in react/type.ts, insieme alle props dei componenti. Da lì li importavano cinque file del motore: i tre mapper e le due utility di proiezione. La dipendenza andava nel verso sbagliato: il motore importava dal render.
Ora stanno in engine/types.ts, e react/type.ts li ri-esporta. Chi scrive un componente continua a importarli da @woodylab/uims/react, che è la porta giusta per lui; il motore non attraversa più il confine.
SlotDefinition, ChildInstance, ResolvedSlot, ResolvedSlots
Compatibilità: quali viewMode possono disegnare un blocco
Il motore è aperto per scelta: un viewMode è riferito per NOME, e niente vieta di accoppiare qualunque blocco a qualunque forma. Il costo di quell’apertura è il silenzio — dai al blocco icon un viewMode che nomina il componente Action e non succede niente di visibile, semplicemente non appare quello che ti aspettavi.
compatibility.ts non chiude l’apertura, la RENDE VISIBILE: sa quale componente ci si aspetta per un tipo di blocco e può dirlo. È materiale per l’interfaccia — l’admin propone prima i viewMode compatibili — non un divieto a runtime.
la relazione blocco → viewMode plausibili
quale componente ci si aspetta per un tipo di blocco, calcolato al caricamento
Il vocabolario degli eventi: due insiemi, nessuno derivato dall’altro
Cosa può dire l’interfaccia è l’unione di due cose diverse. Gli HANDLER sono ciò che esiste e RISPONDE: quelli del pacchetto più quelli del progetto. Il catalogo db.events.json è ciò che è DICHIARATO: i nomi che l’admin propone in una tendina.
I due insiemi non coincidono e non devono. Un handler può esistere senza essere dichiarato — nessuno lo sceglie da interfaccia ma il codice lo può chiamare. Un nome può essere dichiarato senza handler — è il caso degli eventi di stato, che non hanno un handler perché li tratta il reducer. Derivare l’uno dall’altro avrebbe cancellato entrambi i casi legittimi.
l’unione dei due insiemi, per chi deve mostrare un elenco
Le quattro sottocartelle
Il resto del motore è diviso per passaggio: chi decide la forma di un blocco (resolver), chi porta i dati dentro gli slot (mapper), chi calcola le classi (styling), chi legge dal database con la cache giusta (dal). Più tre utility di proiezione che stanno in utils perché le usano tutti.
La dipendenza fra loro è a senso unico: styling non conosce nessuno, mapper usa styling, resolver usa entrambi. Era circolare finché i tre file di stile stavano dentro resolver/, ed è il motivo per cui sono usciti.
tredici file: dal blocco al descrittore
quattro file: dai dati agli slot
tre file: dal viewMode alle classi
tredici file: la lettura cache-aware
tre file: estrazione delle props di UI e proiezione degli slot in React
Continua
Altro in «Il motore · src/engine»
Mapper e slotLa lettura del datoStili e classiTutte le diciassette pagineindice