Semaine 1 – Le codemod
#web #emberjs #codemod #ast #npmView #nodeFs #embroider #vite
La force d’Ember réside dans son amélioration progressive. À mesure que le framework évolue, la communauté ne vous laisse pas de côté : elle vous fournit tout ce dont vous avez besoin pour mettre à jour votre application et adopter les pratiques modernes à votre rythme (croyez-moi, la première version que j’ai utilisée était une 2.x, et j’ai réussi à mettre à jour jusqu’à la 4.x). Mon travail actuel consiste à vous aider à migrer votre application Ember classique vers un build avec Vite. Voici la première étape de mon parcours : le codemod pour aider les développeur·ses à migrer des applications classiques vers des applications Vite.
Qu’est-ce qu’un codemod ? #
Un codemod est essentiellement un script qui transforme automatiquement votre code. Dans l’écosystème Ember, c’est une méthode largement utilisée pour aider les développeur·ses à adopter une nouvelle syntaxe. Par exemple, avant, on utilisait la syntaxe à doubles accolades {{#my-component}} pour invoquer des composants dans un template. Aujourd’hui, on utilise plutôt la syntaxe avec chevrons <MyComponent>. Il existe un codemod qui effectue cette transformation pour vous. Ainsi, si vous devez migrer une ancienne application Ember vers la syntaxe moderne, vous n’avez pas à tout faire manuellement.
Je dois créer un codemod ; par où commencer ? #
Mettre à jour une application Ember classique pour utiliser Vite nécessite de déplacer, renommer et modifier un certain nombre de fichiers. Et comme nous voulons que le nouveau système de build fonctionne à partir de la version 3.28 jusqu’à la dernière version d’Ember (6.x au moment où j’écris ces lignes), je m’attends à devoir gérer de nombreux cas. La tâche semble immense, alors voici quelques approches pour bien démarrer :
Le cas idéal #
Choisissez le cas d’utilisation parfait, qui peut servir de bon point de départ pour la transformation du code. Le terme “parfait” peut avoir différentes significations selon le contexte ; par exemple, il peut signifier “le plus standard”.
🐹 Pour le codemod classique-vers-Vite, le cas idéal serait une toute nouvelle application vide générée avec ember new et initialisée avec la dernière version d’Ember. Ça signifie que l’application est entièrement à jour avec les pratiques modernes et ne contient aucune personnalisation, ce qui en fait le cas de base que je souhaite traiter avant de complexifier davantage.
Le miroir #
Une fois que vous avez identifié le cas d’utilisation parfait, comparez-le avec ce que vous souhaitez obtenir. Placez le code initial et le résultat souhaité côte à côte pour mieux visualiser ce que vous devez accomplir.
🐹 Pour le codemod classique-vers-Vite, je travaille à l’échelle d’une application entière, donc j’utilise un outil comme Beyond Compare. Ce logiciel permet de comparer des dossiers entiers ; je peux voir quels fichiers existent à gauche et à droite, et en cliquant sur un fichier, j’obtiens une comparaison de leur contenu. C’est une approche très pratique : vous exécutez votre codemod dans l’application affichée à gauche, vous rafraîchissez Beyond Compare, et si la comparaison ne montre plus de différences (ou seulement les différences attendues), alors c’est gagné. Combinez ça avec git et une commande reboot personnalisée qui réinitialise l’état du côté gauche à HEAD, et vous obtenez un bon flux de travail de développement.
La transformation #
Une fois que vous avez une vision claire de votre point de départ et de votre objectif, vous devez choisir la meilleure façon de transformer le code. Voici quelques pistes :
-
Un simple remplacement de texte : Si votre cas d’utilisation est relativement “statique” et que vous souhaitez remplacer des chaînes de caractères très spécifiques par d’autres, vous pouvez utiliser des fonctions JavaScript basiques comme
replaceetreplaceAll, ou même des expressions régulières (RegExp). -
Les structures AST : Un arbre de syntaxe abstraite (AST) est une structure de données qui représente un programme sous forme d’arbre de nœuds. Si la partie que vous souhaitez transformer est intégrée dans un code complexe et potentiellement personnalisé, et que son identification dépend de la grammaire du code, alors c’est la méthode à privilégier. L’outil AST Explorer est un “must” pour travailler avec les AST. Vous pouvez y copier-coller n’importe quel code valide et sélectionner les parsers que vous utilisez pour voir l’AST correspondant. Si vous débutez, ce guide sur les plugins Babel offre de bonnes explications (Babel n’est qu’un outil parmi d’autres utilisant les AST, mais le principe s’applique à d’autres contextes comme les règles ESLint, etc.). La bibliothèque recast permet d’éditer votre code en utilisant les AST (1 – lire le fichier avec node fs, 2 – analyser avec recast, 3 – manipuler l’AST avec recast, 4 – imprimer l’AST avec recast, 5 – réécrire le fichier avec node fs).
-
Les commandes Git : Cette approche concerne davantage les initialiseurs que les codemods. Si vous prévoyez que vos utilisateur·rices travaillent avec Git, il existe également des pistes à explorer.
git diffetgit applypermettent d’afficher les différences entre deux fichiers et d’appliquer un patch. Étudier comment les frameworks et les initialiseurs de bibliothèques vous invitent à choisir des options de modèle et proposent des modifications lorsque vos fichiers existants entrent en conflit avec le modèle peut être une piste intéressante.
🐹 Pour le codemod classique-vers-Vite, j’utiliserai probablement un mélange de remplacements de texte et de manipulations d’AST.
Autres conseils #
-
La commande
npm viewpermet d’afficher des informations sur les packages publiés publiquement sur npm. Si vous devez vérifier les mises à jour d’une dépendance, c’est un moyen simple de récupérer ces informations. 🐹 Pour le codemod classique-vers-Vite, je l’utilise pour vérifier si la dernière version d’un addon v1 est un v2 (en utilisant la méta-donnée ’ember-addon’ du package.json). -
Si vous travaillez à l’échelle d’un dossier entier, vous devrez peut-être utiliser le système de fichiers de Node. Deux fonctionnalités utiles sont
process.cwd()etimport.meta.url. La première vous aide à récupérer le répertoire depuis lequel le processus Node est exécuté, et la seconde vous indique dans quel module vous vous trouvez lorsque son code s’exécute. Supposons que j’exécute le codemod dans le dossier de l’application que je souhaite transformer :
// my-codemod/index.js
import { readFile } from 'node\:fs/promises';
import { dirname, join } from 'node\:path';
import { fileURLToPath } from 'node\:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const pkg = JSON.parse(await readFile(join(__dirname, 'package.json'), 'utf8'));
// pkg est le package.json de l'application du codemod
// my-codemod/some-feature.js
let path = join(process.cwd(), 'config/environment.js')
// path pointe vers my-app-to-transform/config/environment.js, car j'exécute le codemod dans le dossier my-app-to-transform
À la fin de la semaine, j’ai exploré des éléments qui seront probablement retravaillés ou non utilisés ; c’est inévitable avec ce type de projet. Je pense qu’une partie de mon travail reste valable, notamment les remplacements de texte dans les fichiers index.html et les transformations AST sur ember-cli-build.js et app.js. Il me faudra une autre semaine pour que mon “cas idéal” fonctionne comme prévu.