Votre projet TypeScript commence a grossir et devenir lent mais vous ne savez pas ce qui provoque ces ralentissements ? Nous allons voir avec un exemple comment dénicher les morceaux qui prennent le plus de temps lors de la compilation.
Quels outils utiliser ?
Nous allons prendre pour exemple
ce repo GitHub. C'est un projet React avec un certain nombre de clés de traduction i18n (8 workspaces de 150 clés, donc 1200 clés).
TypeScript nous propose quelques outils pour investiguer les problèmes de performances liés à la compilation.
Pour commencer, lorsqu'on lance le compilateur TypeScript, on peut lui passer le paramètre extendedDiagnostics.
npx tsc --extendedDiagnostics
donne quelque chose comme :
Files: 90 Lines of TypeScript: 83 Lines of JavaScript: 0 Lines of JSON: 2448 Parse time: 0.42s ResolveModule time: 0.02s ResolveTypeReference time: 0.00s Program time: 0.48s Bind time: 0.20s Check time: 2.64s Emit time: 0.00s Total time: 3.32s Done in 3.53s.
On s'apperçoit que sur ce repo :
Bon, c'est bien de savoir que le type checking prend du temps, mais ce serait mieux de savoir quelles lignes dans le code sont responsables !
Et bien ça tombe bien, on va pouvoir le découvrir en 2 étapes.
D'abord on va générer des fichiers de débug lors de la compilation :
npx tsc --generateTrace <path>
(il faut remplacer <path> par un chemin valide où tsc va créer un dossier, puis écrire les fichiers de débug de la compilation)
Pour lire ce fichier de débug on va utiliser l'onglet "Performance" des outils de développement (dans un navigateur basé sur Chromium).
On y trouvera un bouton "Load profile..." pour charger un fichier de débug (trace.json) :
Une fois le fichier chargé il faudra naviguer tant bien que mal dans l'interface et cliquer sur les morceaux du graphique qui prennent le plus de temps. Par exemple :
Sur l'image on voit qu'une opération de type "checkExpression" dans le fichier App.tsx prend 3.17 secondes
On peut savoir exactement ce à quoi cela correspond dans le code grâce à l'indication : pos 372 end 378
Il faudrait donc ouvrir App.tsx et regarder à partir du caractère 372 jusqu'au caractère 378.
Bon, c'est un peu pénible j'avoue, ce serait bien si un outil pouvait trouver les parties les plus longues de la compilation automatiquement et calculer la ligne / la colonne à notre place...
Bonne nouvelle cet outil
existe !
Pour l'utiliser rien de plus simple :
npx @typescript/analyze-trace <path>
(remplacer <path> par le chemin vers le dossier dans lequel on a généré les fichiers de débug)
Sur notre exemple, cela donnera :
Hot Spots └─ Check file /home/mayeul/Projects/tests/test-tsc/src/App.tsx (3700ms) └─ Check deferred node from (line 17, char 27) to (line 18, char 34) (3186ms) └─ Check expression from (line 18, char 11) to (line 18, char 21) (3172ms) └─ Check expression from (line 18, char 22) to (line 18, char 30) (3170ms) └─ Check expression from (line 18, char 23) to (line 18, char 29) (3170ms)
Si on regarde dans App.tsx la ligne 18, colonne 23 (exactement notre charactère 372 de tout à l'heure), on s'aperçoit que le probleme est dans keys.map()
(le fichier App.tsx)
export const App = () => { const { t } = useTranslation(); const keys: AllI18nKeys[] = [ "anotherTest:anotherTest-100", "hehe:hehe-30", "login:login-103", "common:common-126" ]; return ( <div> <h1>{t("login:login-0")}</h1> {keys.map((key) => ( <p key={key}>{t(key)}</p> ))} </div> ); };
Maintenant on peut se demander pourquoi c'est lent.
Dans le
paragraphe lié aux unions, on s'aperçoit que Microsoft déconseille d'utiliser des unions avec de nombreux éléments, or le type AllI18nKeys est une union de 1200 clés de traduction...
Et voilà ! Maintenant vous êtes capable de trouver d'où viennent les lenteurs de compilation sur votre projet. Par contre pour résoudre ces problèmes, c'est une autre affaire 😉