Errori e fix
Cannot find module dopo `shadcn add`: gli alias sono il problema
Il file viene aggiunto, ma TypeScript non lo trova. Nove volte su dieci l'alias in components.json non corrisponde a quello in tsconfig, oppure manca la dipendenza runtime.
In sintesi
- `components.json` e `tsconfig.json` devono descrivere lo stesso alias: se uno usa `@/` e l'altro `~/`, il file c'è ma TypeScript non lo risolve.
- Dopo `add`, verifica che le dipendenze della ricetta siano state installate: il componente compila solo se le sue import esistono.
- Nei monorepo l'alias deve puntare a `src`, non alla root del pacchetto.
Risposta breve: il componente è stato scritto, ma l'import usa un alias che TypeScript non conosce. Confronta components.json con tsconfig.json: se gli alias non coincidono, il file esiste e non viene risolto.
Perché il file c'è e il compilatore non lo vede
Le due parti prendono decisioni indipendenti. Il CLI scrive il file dove dice aliases, cioè nel punto che l'autore della ricetta ha scelto. TypeScript costruisce il programma da include e rootDir, e risolve gli alias con paths relativo a baseUrl. Se il file finisce fuori da include, tsc non lo legge affatto e l'errore che stampa è Cannot find module, non not part of the project: la diagnosi parte dal messaggio sbagliato, e per questo la correzione di solito tocca l'alias quando il problema era il perimetro.
Le quattro cause
| Causa | Sintomo | Correzione |
|---|---|---|
| Alias diverso tra i due file | import rosso, file presente | allinea `aliases` e `paths` |
| `baseUrl` assente | tutti gli alias falliscono | aggiungi `baseUrl: "."` |
| Dipendenza non installata | errore su un pacchetto | installa le dipendenze elencate |
| Monorepo con alias sulla root | funziona in un pacchetto, non nell'altro | punta a `src` del pacchetto |
Confronto diretto
// components.json
{
"$schema": "https://ui.shadcn.com/schema.json",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui"
}
}
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
Se components.json dice @/components e tsconfig mappa @/* su ./src/*, il file finisce in src/components e l'import funziona. Se uno dei due usa ~/, il file viene scritto in un posto e cercato in un altro.
La sequenza di verifica
- Trova il file appena creato:
find . -name "button.tsx" -not -path "*/node_modules/*". - Apri
components.jsone annota gli alias. - Apri
tsconfig.jsone verifica che ogni alias abbia il suopaths. - Esegui
npx tsc --noEmit: l'errore indica il percorso cercato.
Il comando che dice dove guarda TypeScript
# ogni tentativo di risoluzione, con il percorso cercato
npx tsc --noEmit --traceResolution | grep "button"
# PowerShell
npx tsc --noEmit --traceResolution | Select-String "button"
L'output è lungo e utile: per ogni import elenca i candidati nell'ordine in cui paths li propone, e chiude con la riga che dice se ha risolto. Cerca la prima riga not resolved e leggi il percorso che ha provato. Se il percorso è giusto e il file non c'è, il CLI ha scritto altrove; se il percorso è sbagliato, l'alias è sbagliato. In entrambi i casi il file di configurazione da aprire è deciso dall'output, non dall'intuito.
Quando la correzione non funziona
Se l'alias è corretto e l'errore resta, il problema può essere il resolver del bundler: Vite e Next.js leggono paths in modo diverso e alcuni setup richiedono anche vite-tsconfig-paths. Controlla che il file di configurazione del bundler estenda lo stesso tsconfig. Nei monorepo, un pacchetto può avere il proprio tsconfig e ignorare quello della root.
Quando la correzione tipica peggiora le cose
La correzione che si trova nei forum è aggiungere un secondo schema ai paths: "@/*": ["./src/*", "./*"], così qualunque posizione funziona. Funziona per il caso di oggi e introduce due sorgenti valide per lo stesso nome. TypeScript prende il primo che esiste, il bundler può prendere l'altro, e il progetto inizia a compilare contro una copia vecchia di un componente rimasta nella root da un init precedente. La build passa, la pagina mostra il componente sbagliato, e il sintomo somiglia a una cache. È l'unico caso di questa lista in cui il rosso sparisce e il difetto resta.
Il limite dell'allineamento
Un limite da dichiarare: allineare gli alias risolve la risoluzione, non il contratto della ricetta. Il CLI non riscrive import arbitrari dentro il codice che copia, quindi una ricetta di terze parti può importare un percorso che nel tuo progetto non esiste, o esiste con un altro nome. Il componente compila e manca un pezzo. Dopo ogni add la verifica completa è due comandi, non uno: il controllo dei tipi e l'apertura della pagina.
Come prevenirlo
- Dopo ogni
add, eseguitsc --noEmitin CI: un import rotto non arriva in produzione. - Tieni un solo alias nel progetto: due convenzioni convivono male.
- Nel monorepo, un test che crea un componente vuoto e lo importa dall'app consumatrice.
- Un solo
pathscon una sola voce per alias: due candidati per lo stesso nome sono un guasto che aspetta.
Dove continuare
Per capire il sistema: componenti, temi, blocchi e blocco newsletter. Le guide: temi gratuiti e premium e struttura App Router; il confronto: registry privati, token e namespace.
Domande frequenti
Il file esiste ma l'import è rosso: cosa controllo?
Apri `components.json`, guarda `aliases.components` e `aliases.utils`, poi confrontali con `compilerOptions.paths` in tsconfig. Devono descrivere lo stesso percorso.
Perché funziona con Vite e non con Next.js?
I due progetti hanno alias iniziali diversi e un tsconfig diverso. La ricetta usa l'alias dichiarato in components.json: se manca, applica quello di default.
Devo riavviare il dev server?
Per un file nuovo spesso sì, perché il resolver tiene una cache dei percorsi. Se dopo il riavvio resta rosso, il problema è di configurazione, non di cache.
Cosa significa `Missing dependency` nell'output?
Che la ricetta importa un pacchetto non presente nel progetto. Installa quello che il comando elenca, poi riprova.
Posso spostare i componenti fuori da `src`?
Sì, ma aggiorna alias e `content` di Tailwind insieme. Spostare solo i file produce componenti che esistono e non vengono stilizzati.