


Création d'un outil Codemod pour réécrire les exportations par défaut
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.
La motivation ressemblait à ceci :
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 :
- résoudre toutes les importations dans le projet et enregistrer les relations entre les fichiers
- recueillir des informations sur l'importation/exportation par défaut
- créer un nouveau nom d'identifiant pour l'export nommé
- remplacer toutes les entrées du dépôt ?
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
Processus de développement
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
- J'ai introduit une variable d'environnement, IS_GATHER_INFO, pour contrôler cette phase. Le script utilise la résolution pour trouver chaque utilisation d'une exportation/importation par défaut
- Une autre var d'environnement ENTRY contient un chemin relatif vers votre point d'entrée de base de code, à partir de ce fichier, toutes les importations seront résolues et analysées
-
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.
- J'ai introduit une variable d'environnement, IS_TRANSFORM, pour contrôler cette phase
En se divisant en ces deux étapes :
- J'ai pu dissocier la collecte de données de la transformation, réduisant ainsi la quantité de code exécuté et le temps passé pendant le développement et le débogage.
- C'est un moyen très pratique de voir le résultat de la fonction rassemblerInfo, de l'analyser, de réexécuter votre code
- Testez les transformations sans exécuter à plusieurs reprises l'ensemble du pipeline avec les données collectées
- La collecte du vidage des données est utile si vous devez exécuter cet outil pour différents points d'entrée mais réutiliser les données collectées
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 :
-
ImportDefaultSpecifier pour rechercher uniquement les instructions d'importation par défaut
- importer quelque chose de '...'
-
ExportDefaultDeclaration pour rechercher uniquement les instructions d'exportation par défaut
- exporter quelque chose par défaut ;
-
ExportNamedDeclaration pour rechercher les instructions d'importation par défaut et d'exportation par défaut
- exporter { quelque chose par défaut } depuis '...' - exportation par défaut
- exporter { par défaut comme quelque chose } depuis '...' - importation par défaut
- export { default } from '...' - importation par défaut et exportation par défaut simultanément
-
ImportExpression pour rechercher l'importation dynamique et marquer ce fichier comme nécessaire pour conserver l'exportation par défaut. Certains outils comme React.lazy fonctionnent uniquement avec l'exportation par défaut.
- importer('...')
- De plus, j'ai enregistré des informations sur les fichiers proxy, ce sont des fichiers qui importent quelque chose par défaut et exportent ce quelque chose par défaut
- Utilisé pour trouver le nouveau nom de l'export nommé dans n'importe quel fichier : fichier a -> fichier b -> fichier c
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.
Links
- jscodeshift
- astexplorer
Ç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!

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

Video Face Swap
Échangez les visages dans n'importe quelle vidéo sans effort grâce à notre outil d'échange de visage AI entièrement gratuit !

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Sujets chauds











Python convient plus aux débutants, avec une courbe d'apprentissage en douceur et une syntaxe concise; JavaScript convient au développement frontal, avec une courbe d'apprentissage abrupte et une syntaxe flexible. 1. La syntaxe Python est intuitive et adaptée à la science des données et au développement back-end. 2. JavaScript est flexible et largement utilisé dans la programmation frontale et côté serveur.

Le passage de C / C à JavaScript nécessite de s'adapter à la frappe dynamique, à la collecte des ordures et à la programmation asynchrone. 1) C / C est un langage dactylographié statiquement qui nécessite une gestion manuelle de la mémoire, tandis que JavaScript est dynamiquement typé et que la collecte des déchets est automatiquement traitée. 2) C / C doit être compilé en code machine, tandis que JavaScript est une langue interprétée. 3) JavaScript introduit des concepts tels que les fermetures, les chaînes de prototypes et la promesse, ce qui améliore la flexibilité et les capacités de programmation asynchrones.

Les principales utilisations de JavaScript dans le développement Web incluent l'interaction client, la vérification du formulaire et la communication asynchrone. 1) Mise à jour du contenu dynamique et interaction utilisateur via les opérations DOM; 2) La vérification du client est effectuée avant que l'utilisateur ne soumette les données pour améliorer l'expérience utilisateur; 3) La communication de rafraîchissement avec le serveur est réalisée via la technologie AJAX.

L'application de JavaScript dans le monde réel comprend un développement frontal et back-end. 1) Afficher les applications frontales en créant une application de liste TODO, impliquant les opérations DOM et le traitement des événements. 2) Construisez RestulAPI via Node.js et Express pour démontrer les applications back-end.

Comprendre le fonctionnement du moteur JavaScript en interne est important pour les développeurs car il aide à écrire du code plus efficace et à comprendre les goulots d'étranglement des performances et les stratégies d'optimisation. 1) Le flux de travail du moteur comprend trois étapes: analyse, compilation et exécution; 2) Pendant le processus d'exécution, le moteur effectuera une optimisation dynamique, comme le cache en ligne et les classes cachées; 3) Les meilleures pratiques comprennent l'évitement des variables globales, l'optimisation des boucles, l'utilisation de const et de locations et d'éviter une utilisation excessive des fermetures.

Python et JavaScript ont leurs propres avantages et inconvénients en termes de communauté, de bibliothèques et de ressources. 1) La communauté Python est amicale et adaptée aux débutants, mais les ressources de développement frontal ne sont pas aussi riches que JavaScript. 2) Python est puissant dans les bibliothèques de science des données et d'apprentissage automatique, tandis que JavaScript est meilleur dans les bibliothèques et les cadres de développement frontaux. 3) Les deux ont des ressources d'apprentissage riches, mais Python convient pour commencer par des documents officiels, tandis que JavaScript est meilleur avec MDNWEBDOCS. Le choix doit être basé sur les besoins du projet et les intérêts personnels.

Les choix de Python et JavaScript dans les environnements de développement sont importants. 1) L'environnement de développement de Python comprend Pycharm, Jupyternotebook et Anaconda, qui conviennent à la science des données et au prototypage rapide. 2) L'environnement de développement de JavaScript comprend Node.js, VScode et WebPack, qui conviennent au développement frontal et back-end. Le choix des bons outils en fonction des besoins du projet peut améliorer l'efficacité du développement et le taux de réussite du projet.

C et C jouent un rôle essentiel dans le moteur JavaScript, principalement utilisé pour implémenter des interprètes et des compilateurs JIT. 1) C est utilisé pour analyser le code source JavaScript et générer une arborescence de syntaxe abstraite. 2) C est responsable de la génération et de l'exécution de bytecode. 3) C met en œuvre le compilateur JIT, optimise et compile le code de point chaud à l'exécution et améliore considérablement l'efficacité d'exécution de JavaScript.
