Récemment au travail, nous avons décidé de migrer vers les exportations/importations nommées et d'ajouter la règle eslint no-default-export.
Les exportations par défaut peuvent rendre le code plus difficile à maintenir, en particulier dans les bases de code volumineuses. Les noms importés peuvent être différents pour la même entité, affectant le processus de lecture du code et l'écriture des analyseurs statiques, ce qui rend la tâche plus difficile. A l’inverse, passer aux exports nommés supprime tous les inconvénients des exports par défaut.
Bien sûr, nous avons une énorme base de code et ce n'est pas un travail intéressant de remplacer manuellement ~1500 exportations par défaut et ~12000 importations par défaut ?
La principale difficulté était de mettre à jour tous les fichiers liés avec le même nouvel identifiant, créé pour l'export nommé.
Je vous donne un exemple :
// Button/Button.tsx const Button = () => {}; export default Button; // Button/index.ts export { default } from './Button.tsx'; // SomePage1.tsx import OldButton from './component/Button'; // SomePage2.tsx import TestButton from './component/Button';
Et le résultat cible que je suppose ressemblerait à ceci :
// Button/Button.tsx export const Button = () => {}; // Button/index.ts export { Button } from './Button.tsx'; // SomePage1.tsx import { Button as OldButton } from './component/Button'; // SomePage2.tsx import { Button as TestButton } from './component/Button';
Chaque solution que j'ai trouvée sur Internet n'était qu'un codemod pour transformer chaque fichier indépendamment sans rien connaître d'autre en dehors de ce fichier.
J'ai commencé à rêver d'un analyseur qui :
J'ai donc relevé un nouveau défi : développer un outil codemod qui réécrit automatiquement les exportations/importations par défaut en celles nommées.
Je l'ai déjà développé ! ? ? spoiler
Premières réflexions
Cela s'est produit juste après mon expérience précédente Visualiser l'arborescence des composants de réaction et la première idée était de réutiliser les plugins babel et webpack pour parcourir tous les modules et analyser l'AST, mais pourquoi, si jscodeshift a déjà l'analyseur, et si j'ai trouvé un remplaçant pour le plugin webpack, je serais capable d'écrire un outil indépendant du bundler, super ?
Outils
Ok, j'ai un jscodeshift comme analyseur. Mais pour trouver les relations entre tous les fichiers à partir du point d'entrée, j'ai trouvé le package de résolution, qui aide à résoudre des chemins comme les nodejs natifs require.resolve, mais il est plus similaire à la résolution de chemins comme les bundlers, vous avez plus de contrôle sur les extensions, la synchronisation /comportement asynchrone, etc.
Ingénierie du processus en deux étapes
La version initiale de mon outil ressemblait à tout dans un seul script. Cependant, pour améliorer la flexibilité et les performances et également simplifier le processus de développement avec le débogage, j'ai refactorisé l'outil en deux étapes :
Collecte de données : La première phase rassemble toutes les instances d'importations et d'exportations par défaut dans la base de code
Transformation : Une fois les données collectées, la deuxième phase réécrit les exports par défaut en exports nommés. Grâce à jscodeshift, j'ai transformé le code source en parallèle et facilement.
En se divisant en ces deux étapes :
Au fur et à mesure que les cas commençaient à s'accumuler (comme les importations dynamiques, les valeurs par défaut réexportées, différentes entités exportées : variables, fonctions et classes, et les noms déjà utilisés de problèmes de variables), j'ai passé plus de temps à configurer des cas de test. En 30 minutes environ, j'avais une configuration de test solide, me permettant de passer au développement piloté par les tests (TDD). Croyez-moi, cela vaut la peine de consacrer du temps au TDD pour de tels outils, qui ont un nombre énorme de cas. Plus vous avancez, plus vous ressentez de la valeur dans vos cas de test. Je dirais qu'après avoir couvert la moitié des cas, si vous n'avez pas de tests, cela devient un cauchemar d'exécuter et de déboguer sur un énorme projet car chaque fois que vous devez ajouter des modifications, cela peut casser beaucoup d'autres cas.
AST :
J'ai utilisé les types de nœuds AST suivants :
Considérations techniques et limitations connues
Bien que l'outil soit fonctionnel, il existe certains cas extrêmes qu'il ne gère pas encore :
utilisation de l'espace de noms.default
le code suivant ne sera pas encore transformé :
// Button/Button.tsx const Button = () => {}; export default Button; // Button/index.ts export { default } from './Button.tsx'; // SomePage1.tsx import OldButton from './component/Button'; // SomePage2.tsx import TestButton from './component/Button';
Conflits dans les fichiers proxy
source :
// Button/Button.tsx export const Button = () => {}; // Button/index.ts export { Button } from './Button.tsx'; // SomePage1.tsx import { Button as OldButton } from './component/Button'; // SomePage2.tsx import { Button as TestButton } from './component/Button';
résultat :
import * as allConst from './const'; console.log(allConst.default);
Exportations foirées comme
source :
export { Modals as default } from './Modals'; export { Modals } from './Modals';
entraînera une logique brisée, car il y a maintenant deux exportations identiques avec une implémentation différente :
export { Modals } from './Modals'; export { Modals } from './Modals';
Et les importations pour l'entité précédente doivent également être corrigées manuellement
source :
export class GhostDataProvider {} export default hoc()(GhostDataProvider);
résultat :
export class GhostDataProvider {} const GhostDataProviderAlias = hoc()(GhostDataProvider); export { GhostDataProviderAlias as GhostDataProvider };
Malgré ces limitations, j'ai corrigé manuellement le reste des erreurs en 15 à 20 minutes et j'ai réussi à lancer notre vrai projet. Les exportations par défaut de réécriture.
Ça y est, bienvenue dans les commentaires ci-dessous ! ?
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!