Da Gatsby ad Astro - La struttura del progetto

Dopo aver spiegato nell’introduzione i motivi che mi hanno spinto a migrare il mio sito da Gatsby ad Astro, iniziamo ad addentrarci in alcuni degli aspetti tecnici.

Prima di tutto, è buono specificare che, per avere un codice più pulito e aggiornato, ho deciso di riscrivere interamente il mio sito, mantenendo lo stile e le funzionalità del precedente (oltre ad aggiungere qualche cosina che avevo in cantiere da tempo). Quando ho scritto la prima bozza di questo articolo, avevo in programma di dire che il lavoro non era ancora finito e che sarebbe continuato per tutta la durata di questa serie; in realtà, è stato più veloce di quanto mi aspettassi! Il sito che state vedendo è già stato migrato interamente ad Astro 🥳.

Per creare un nuovo progetto con Astro, è sufficiente lanciare un comando dal terminale:

npm create astro@latest

Questo comando farà partire un configuratore molto intuitivo che permetterà di scegliere alcune impostazioni utili. Nel mio caso ho scelto typescript come linguaggio e gli ho permesso di includere dei file d’esempio (come da impostazione predefinita).

La struttura del progetto

Dopo aver creato il nostro progetto, ci troveremo con una struttura simile a questa (alcuni file e cartelle le ho omesse per praticità):

📂 root
├ 📂 public
├ 📂 src
│ └ 📂 pages
│   └ 📄 index.astro
└ 📄 astro.config.mjs

Queste sono le nostre cartelle principali, a cui aggiungeremo anche la cartella content e assets (entrambe all’interno della cartella src). In breve, ecco una descrizione delle cartelle, dei file e del loro utilizzo:

File / CartellaDescrizione
📂 publicCartella contenente i file statici che verranno copiati senza modifiche
📂 srcCartella contenente tutto il codice del nostro sito, compreso anche il contenuto (blog post e simili)
📂 pagesCartella che contiene tutte le pagine del nostro sito (home, chi siamo, …), comprese quelle dinamiche (template che genereranno i singoli articoli, post, pagine degli autori, …)
📄 index.astroEssendo nella cartella pages, questa è la Homepage del nostro sito
📂 contentCartella che si trova dentro a src e racchiude tutti i contenuti del nostro sito, come gli articoli, i nostri progetti, o altre collezioni
📂 assetsCartella che si trova dentro a src e viene usata da Astro per ottimizzare e condividere più facilmente alcune risorse, in particolare i media
📄 astro.config.mjsFile di configurazione di Astro, dove andremo ad aggiungere le impostazioni dei plugin e altre informazioni

Con i file di esempio, troverete anche una cartella layouts e una cartella components. Queste cartelle sono una convenzione, non devono per forza avere questo nome. Nella cartella layouts vengono contenuti tutti i componenti che servono come template per le pagine, gli articoli, o altro. Di solito, nelle pagine, vengono utilizzati più o meno così:

<PageLayout>
    <h1>Pagina d'esempio</h1>
    <p>Lorem ipsum dolor sit amet ...</p>
</PageLayout>

Nella cartella components vengono invece raccolti tutti i componenti di Astro, React o simili, che useremo nelle nostre pagine e nei nostri articoli.

La configurazione nel file astro.config.mjs

Questo file ci permette di configurare il funzionamento di Astro e delle integrazioni (plugin). In certi casi, quando s’installa una integrazione, viene automaticamente suggerita una modifica a questo file che possiamo applicare dando semplicemente l’ok.

Nel caso questo non succeda, possiamo seguire le istruzioni che troviamo nella documentazione di Astro o in quella delle varie integrazioni. Al momento non mi è capitato di avere bisogno di modificare questo file (avevo solo attivato una funzionalità sperimentale che però è stata pubblicata ufficialmente con la versione 3 di Astro, e quindi non ho più bisogno nemmeno di quella).

La cartella pages e le routes

Quando creiamo un file all’interno della cartella pages, stiamo creando una nuova pagina per il nostro sito (anche chiamata route). L’indirizzo url a cui questa pagina sarà accessibile corrisponde al percorso del file. Per esempio, il percorso miosito.it/programmazione/javascript/astro/tutorial corrisponderà alla seguente struttura all’interno della cartella pages:

📂 pages
└ 📂 programmazione
  └ 📂 javascript
    └ 📂 astro
      └ 📄 tutorial.astro

Possiamo anche creare delle route dinamiche. Questo significa che da un solo file possiamo creare più pagine, sfruttando una funzione javascript che ci fornisce i dati necessari. Per esempio, per permettere l’accesso agli articoli di un blog, sarà necessario creare questa struttura:

📂 pages
└ 📂 blog
  └ 📄 [slug].astro

All’interno di questo file, dovremo esportare una funzione asincrona chiamata getStaticPaths, che deve dare come valore di ritorno una lista di oggetti simile a questa:

[
    {
        params: {
            slug: 'primo-articolo',
            props: {
                title: 'Primo articolo',
                content: 'Questo è il contenuto del mio primo articolo...'
            }
        }
    },
]

Possiamo creare questa lista come vogliamo, ma Astro ci consente di estrarre queste informazioni direttamente da dei file markdown, MDX o di altri tipi. Questo è reso possibile grazie alle collezioni.

La cartella content e le collezioni

In Astro esiste il concetto di “Content collections”: un modo per categorizzare i contenuti del sito. Queste collezioni possono essere sfruttate all’interno delle pagine, sia statiche che dinamiche, per mostrare i dati.

Nel mio blog sto utilizzando al momento due collezioni: i post (come questo) e i progetti. Per ognuna delle collezioni possiamo anche stabilire alcune regole in modo che i dati vengano validati da Astro al momento della creazione del sito (per esempio, possiamo assicurarci che ogni post abbia una data di creazione).

Tutti i file che fanno parte di una Content collection devono essere salvati all’interno della cartella src/content. Qui possiamo inserire vari tipi di file, nel mio caso sto utilizzando dei file .mdx (un tipo particolare di Markdown che consente l’aggiunta di componenti Javascript).

Fortunatamente, la struttura che utilizzavo per salvare i miei post in Gatsby funziona perfettamente anche in Astro v3:

📂 content
├ 📂 blog
│ └ 📂 nome-articolo
│   ├ 📄 index.astro
│   └ 📄 cover.png
└ 📄 config.ts

Prima di tutto, vediamo che all’interno della cartella content c’è una cartella blog. I contenuti di ogni collezione, infatti, vanno inseriti in una cartella specifica. In questo caso, avendo creato una cartella di nome “blog”, avrò a disposizione una collezione chiamata “blog” da usare nel sito.

Nella collezione blog, ogni articolo ha la sua cartella, il cui nome viene usato per decidere lo “slug” del nostro post (cioè il nome del post nell’URL). Al suo interno troviamo un file index.astro che contiene il testo, oltre ad eventuali immagini, gif o simili che utilizzerò in quello specifico articolo.

Questa struttura mi permette di conservare il testo dei miei post insieme alle immagini e agli altri asset, e la trovo molto pratica.

Ricordate comunque che questi file non vengono automaticamente convertiti in pagine web, ma consentono ad Astro di rendere questi contenuti disponibili nel resto del nostro codice. Questa tecnica è utile perché ci permette di creare insiemi di dati senza essere troppo opinionata su come vogliamo usarli, infatti non è detto che vorremo creare pagine web per ogni tipo di collezione nel nostro sito.

Nell’esempio mostrato sopra potete vedere anche un file speciale: il file config.ts. Questo è il posto dove possiamo indicare le regole che i nostri contenuti devono seguire. Per esempio, possiamo richiedere che nella frontmatter dei post sia presente una proprietà description, un’immagine che sia maggiore di una specifica dimensione, e altre cose simili.

Questo file deve esportare una variabile collections (mi raccomando il plurale!) contenente tutte le specifiche. Ecco un esempio di file config.ts:

import { z, defineCollection } from 'astro:content';

const blogCollection = defineCollection({
    type: 'content',
    schema: z.object({
        title: z.string(),
        description: z.string(),
        date: z.date(),
        published: z.boolean(),
        categories: z.array(z.string()),
    })
});

export const collections = {
    'blog': blogCollection,
}

Con questo codice utilizziamo Zod per stabilire che ogni elemento della collezione “blog”, nella sua frontmatter, deve contenere le proprietà title, description, date, published e categories.

In seguito, potremo utilizzare questi dati per decidere se e come mostrare i nostri post.

La cartella assets

In questa cartella possiamo inserire tutte le immagini che verranno utilizzate nel nostro sito. Dato che le immagini che uso nei miei articoli le conservo vicino all’articolo stesso nella cartella content, in questa cartella conservo le immagini che vengono usate in altre parti del sito, come l’header, il footer o le pagine statiche.

Fino alla versione 2 di Astro, questa cartella era necessaria per fare uso della nuova API sperimentale che avrebbe semplificato l’utilizzo dei media nel sito, permettendo anche di ottimizzarli in maniera automatica. Dalla versione 3, invece, è rimasta semplicemente come una convenzione, dato che è possibile importare ed ottimizzare qualsiasi media, a prescindere dalla cartella.

Nel prossimo articolo di questa serie vedremo l’anatomia di un componente di Astro e in quali posti possiamo sfruttare questi componenti.

A presto!