Le mode d'importation peut se déplacer librement pour sentir la puissance du plug - in Babel

le mode importation peut se


Quand nous import Quand un module,Ceci peut être introduit par défaut:

import path from 'path';
path.join('a', 'b');
function func() {
const sep = 'aaa';
console.log(path.sep);
}
Copier le Code

Il est également possible de déconstruire l'introduction de:

import { join, sep as _sep } from 'path';
join('a', 'b');
function func() {
const sep = 'aaa';
console.log(_sep);
}
Copier le Code

La première importation par défaut est appelée default import,La deuxième introduction de déconstruction s'appelle named import.

Je ne sais pas quel genre de.

S'il y a un besoin,Laissez - vous prendre tous les default import Convertir en named import,Que feriez - vous??

Peut - être diriez - vous que ce n'est pas tout ce qu'il faut pour introduire des variables,Modifier pour appeler directement la méthode,Puis ces noms de méthodes sont écrits de façon déconstructive dans import Dans la déclaration?.

Mais si les éléments à modifier sont 100 Et plusieurs de ces fichiers?(Déclencheur treeshking C'est ce qu'il faut changer)

C'est le moment d'envisager babel Le plug - in est là., C'est parfait pour faire cette modification automatique régulière et massive de code .

Voyons par exemple babel La puissance du plug - in .

Parce qu'il y a plus de code , Tout le monde n'a peut - être pas la patience de regarder , Voyons d'abord l'effet :

Effets des tests

Voici le Code d'entrée :

import path from 'path';
path.join('a', 'b');
function func() {
const sep = 'aaa';
console.log(path.sep);
}
Copier le Code

Nous introduisons babel Plug - in, Lire le Code d'entrée et faire la conversion :

const { transformFileSync } = require('@babel/core');
const importTransformPlugin = require('./plugin/importTransform');
const path = require('path');
const { code } = transformFileSync(path.join(__dirname, './sourceCode.js'), {
plugins: [[importTransformPlugin]]
});
console.log(code);
Copier le Code

Imprimer comme suit:

C'est fait. default import À named import Conversion automatique de .

Peut - être que certains camarades de classe s'inquiètent du problème du surnom ,On va tester:

Je vois., Le plug - in a traité le problème du double nom .

Analyse des idées

import La partie médiane de l'énoncé s'appelle specifier,On peut passer par astexplorer.net Pour voir visuellement AST.

Comme celui - ci import Déclarations:

import React, {useState as test, useEffect} from 'react';
Copier le Code

Ça correspond à AST C'est comme ça:

C'est - à - dire par défaut import - Oui. ImportDefaultSpecifier, Et la déconstruction import - Oui. ImportSpecifier

ImportSpecifier L'énoncé a local Et imported Propriétés, Représente respectivement le nom introduit et le nom renommé :

Alors notre but est clair ,C'est ça. ImportDefaultSpecifier Convertir en ImportSpecifier, Et la méthode de propriété utilisée pour définir imported Propriétés, Si vous avez besoin de renommer, définissez local Propriétés.

Comment savoir quelles méthodes d'attribut sont utilisées ? C'est - à - dire comment analyser les références aux variables ?

babel Offre scope De api, Pour l'analyse de la portée , Vous pouvez obtenir la déclaration dans le champ d'application , Et toutes les références à cette déclaration .

Par exemple, on peut utiliser ici scope.getBinding Méthode pour obtenir la Déclaration de la variable :

const binding = scope.getBinding('path');
Copier le Code

Et utiliser binding.references Pour obtenir toutes les références à cette déclaration ,C'est - à - dire path.join Et path.sep.

Ensuite, vous pouvez changer ces deux références en un appel de méthode direct , Puis modifiez import L'instruction est prête pour la déconstruction .

Résumons les étapes :

  • Trouver import Dans une déclaration ImportDefaultSpecifier
  • Prends ça. ImportDefaultSpecifier Déclaration sur le champ d'application (binding)
  • Trouvez toutes les références à cette déclaration (reference)
  • Modifier la référence partout sous la forme d'une fonction d'appel direct , Collecte des noms de fonctions
  • S'il y a une variable avec un double nom dans le champ d'application , Génère un nom de fonction unique
  • Modifier en fonction du nom de la fonction collectée ImportDefaultSpecifier Pour ImportSpecifier

Le principe est passé à peu près , On va écrire le Code

Mise en œuvre du Code

babel Un plug - in est la forme d'un objet de retour de fonction , Les objets retournés sont principalement via visitor Attribut pour spécifier ce qui AST Que faire avec .

On va faire un tour babel Le squelette du plug - in :

const { declare } = require('@babel/helper-plugin-utils');
const importTransformPlugin = declare((api, options, dirname) => {
api.assertVersion(7);
return {
visitor: {
ImportDeclaration(path) {
}
}
}
});
module.exports = importTransformPlugin;
Copier le Code

Ce qu'on va faire ici, c'est import Déclarations ImportDeclaration.

@babel/helper-plugin-utils Enveloppé declare La méthode fonctionne pour api Agrandir un assertVersion Méthodes.Et assertVersion Si ce plugin fonctionne babel6 Il y a une erreur qui dit que ce plug - in ne peut être utilisé que pour babel7, Peut éviter les erreurs de journal ne peut pas lire .

path C'est pour le fonctionnement AST Certains api, Et il est resté node Liens entre,Par exemple, parent、sibling Attendez..

Passons au sujet:

On doit d'abord sortir specifiers Partie de,Et découvrir ImportDefaultSpecifier

ImportDeclaration(path) {
// Trouver import Dans une déclaration default import
const importDefaultSpecifiers = path.node.specifiers.filter(item => api.types.isImportDefaultSpecifier(item));
// Pour chaque default import Faire la conversion
importDefaultSpecifiers.forEach(defaultSpecifier => {
});
}
Copier le Code

Et pour chaque default import Trouver toutes les références basées sur les déclarations dans le champ d'application :

 // import Nom de la variable
const importId = defaultSpecifier.local.name;
// Déclaration de la variable 
const binding = path.scope.getBinding(importId);
binding.referencePaths.forEach(referencePath=> {
});
Copier le Code

Ensuite, pour chaque référence à ce import Tout a été modifié , Au lieu de cela, appelez la fonction directement , Et recueillir les noms des fonctions .Ce qui est important ici, c'est que, S'il y a une variable du même nom dans le champ d'application, une nouvelle variable unique doit être générée id.

// Déclaration de la variable 
const binding = path.scope.getBinding(importId);
const referedIds = [];
const transformedIds = [];
// Recueillir le nom de la méthode pour tous les endroits où la déclaration est référencée 
binding.referencePaths.forEach(referencePath=> {
const currentPath = referencePath.parentPath;
const methodName = currentPath.node.property.name;
// Nom de la méthode précédente 
referedIds.push(currentPath.node.property);
if (!currentPath.scope.getBinding(methodName)) {// Si le champ d'application n'a pas de variable de renommage 
const methodNameNode = currentPath.node.property;
currentPath.replaceWith(methodNameNode);
transformedIds.push(methodNameNode); // Nom de la méthode convertie 
} else {// Si le champ d'application a une variable de nom double 
const newMethodName = referencePath.scope.generateUidIdentifier(methodName);
currentPath.replaceWith(newMethodName);
transformedIds.push(newMethodName); // Nom de la méthode convertie 
}
});
Copier le Code

Cette partie de la logique est plus ,C'est important..

Nous devons enregistrer la méthode référencée pour chaque référence à la variable ,Par exemple, path.join、path.sep C'est une citation join Et sep Méthodes.

Et puis il va mettre path.join Remplacer par join,Prends ça. path.sep Remplacer par sep.

S'il y en a dans le champ d'application join Ou sep Déclaration, Un nouveau id, Et enregistrer le nouveau id Qu'est - ce que c'est?.

Tous les noms de méthodes ont été recueillis ,Peut être modifié import La déclaration est:

// Conversion import L'énoncé est: named import
const newSpecifiers = referedIds.map((id, index) => api.types.ImportSpecifier(transformedIds[index], id));
path.node.specifiers = newSpecifiers;
Copier le Code

Non. babel La base du plug - in peut sembler un peu étourdie ,C'est bon, Il suffit de savoir ce qu'il fait . On va essayer l'effet .

Penser et coder

On l'a fait. default import À named import Conversion automatique de , En fait, c'est l'inverse , Non, c'est aussi l'analyse scope De binding Et reference,Et ensuite aller modifier AST - Oui.? Les étudiants intéressés peuvent essayer de changer à l'envers comment écrire .

Le code complet du plug - in est le suivant :


const { declare } = require('@babel/helper-plugin-utils');
const importTransformPlugin = declare((api, options, dirname) => {
api.assertVersion(7);
return {
visitor: {
ImportDeclaration(path) {
// Trouver import Dans une déclaration default import
const importDefaultSpecifiers = path.node.specifiers.filter(item => api.types.isImportDefaultSpecifier(item));
// Pour chaque default import Faire la conversion
importDefaultSpecifiers.forEach(defaultSpecifier => {
// import Nom de la variable
const importId = defaultSpecifier.local.name;
// Déclaration de la variable 
const binding = path.scope.getBinding(importId);
const referedIds = [];
const transformedIds = [];
// Recueillir le nom de la méthode pour tous les endroits où la déclaration est référencée 
binding.referencePaths.forEach(referencePath=> {
const currentPath = referencePath.parentPath;
const methodName = currentPath.node.property.name;
// Nom de la méthode précédente 
referedIds.push(currentPath.node.property);
if (!currentPath.scope.getBinding(methodName)) {// Si le champ d'application n'a pas de variable de renommage 
const methodNameNode = currentPath.node.property;
currentPath.replaceWith(methodNameNode);
transformedIds.push(methodNameNode); // Nom de la méthode convertie 
} else {// Si le champ d'application a une variable de nom double 
const newMethodName = referencePath.scope.generateUidIdentifier(methodName);
currentPath.replaceWith(newMethodName);
transformedIds.push(newMethodName); // Nom de la méthode convertie 
}
});
// Conversion import L'énoncé est: named import
const newSpecifiers = referedIds.map((id, index) => api.types.ImportSpecifier(transformedIds[index], id));
path.node.specifiers = newSpecifiers;
});
}
}
}
});
module.exports = importTransformPlugin;
Copier le Code

Résumé

On va le faire. default import Tourne. named import,C'est - à - dire ImportDefaultSpecifier Tourne. ImportSpecifier,Pour passer scope De api Analyse binding Et reference, Trouver toutes les références , Remplacer par un appel direct à la fonction , Et puis on va modifier import Statement AST C'est bon.

babel Les plug - ins sont particulièrement adaptés pour répondre à cette demande régulière et à forte conversion , C'est très puissant dans certaines scènes .

版权声明
本文为[ZxgDieu dit qu'il doit y avoir de la lumière.]所创,转载请带上原文链接,感谢
https://qdmana.com/2021/10/20211013231026761H.html

  1. 为什么说 Node.js 是实时应用程序开发的绝佳选择
  2. PaddlePaddle:在 Serverless 架构上十几行代码实现 OCR 能力
  3. 使用elementui在完成项目中遇到的未知知识点2
  4. On the mechanism of webpack loader
  5. 云原生体系下 Serverless 弹性探索与实践
  6. vue开发技巧
  7. Une fleur merveilleuse de l'histoire de l'industrie des nouveaux véhicules énergétiques, Zhongtai Jiangnan T11, une voiture vintage que vous n'avez jamais vue
  8. 致敬!再见了!LayUI !
  9. Vue安装和卸载
  10. Implement a flipped character with the transform attribute of CSS
  11. 你的第一个 Docker + React + Express 全栈应用
  12. [apprentissage de l'algorithme] 1486. Fonctionnement exclusif du tableau (Java / C / C + + / python / go / Rust)
  13. Zhang Daxian sends a blessing video on xYG relay, showing positive energy in details
  14. 前端技巧-JS元编程ES6 symbol公开符号
  15. Article de 37 ans seul à l'hôpital!Il boitait, soupçonnait d'être blessé, souriait avec douleur
  16. 前端推荐!10分钟带你了解Konva运行原理
  17. npm ERR! iview-project@3.0.0 init: `webpack --progress --config webpack.dev.config.js
  18. 零基础学习Web前端需要注意什么呢?
  19. The Youth League promotes Yiyang Qianxi new film, and the relationship between the two generation and the generation is good. Li Fei is blessed.
  20. PaddlePaddle:在 Serverless 架构上十几行代码实现 OCR 能力
  21. JavaScript数组 几个常用方法
  22. Qu'est - ce qu'il faut remarquer à l'avant - plan Web de l'apprentissage de base zéro?
  23. 暢談this的四種綁定方式
  24. 2021最新Vue面试必胜宝典,大厂面试题解析!
  25. Quatre façons de lier ceci
  26. Préparation au développement de l'extension tagdown
  27. Intervieweur: Parlez - moi des flotteurs CSS
  28. Packaging the View Component Library with rollup
  29. Comment un composant enfant modifie les valeurs passées par le composant parent
  30. Résumé de l'API Express
  31. Optimisation de la structure du Code if else dans le projet
  32. Fonction magique pour résoudre le problème de la fonction maybe - - fonction either
  33. 新手学前端的方法是什么 自学前端该怎么规划
  34. 云原生体系下 Serverless 弹性探索与实践
  35. 如何全方位打造安全高效的HTTPS站点(一)
  36. "Liu Jing dit che 丨 point de vue" est - ce que Custom Road est un MpV digne de la terre?
  37. 从理念到LRU算法实现,起底未来React异步开发方式
  38. Compared with Volvo XC60, Lingke 09 goes out of the spa platform. What would you choose, regardless of the brand?
  39. PaddlePaddle:在 Serverless 架构上十几行代码实现 OCR 能力
  40. 云原生体系下 Serverless 弹性探索与实践
  41. 初学者怎么学Web前端?
  42. react
  43. PaddlePaddle:在 Serverless 架构上十几行代码实现 OCR 能力
  44. JavaScript数组 几个常用方法
  45. Angular 依赖注入 - 全面解析
  46. html_day02
  47. 那些年我们前端面试中经常被问到的题!
  48. The starting price of Ducati multistada V2 in North America is less than 100000 yuan
  49. Hls.js 使用文檔
  50. Hls.js travailler avec des documents
  51. Problèmes liés à la précision JS
  52. Copie une partie des propriétés d'un objet à un autre objet
  53. Multiplexage de modules en vuex
  54. Développement multilingue Android, questions d'entrevue pour le développement de logiciels Android
  55. Chen lushai and her best friend Wang Meng play video, fearless of the pressure of public opinion, and in a good mood to dance in a bare back
  56. # Sass速通(四):继承、混合与函数
  57. Vidéo de développement de combat Android, questions d'entrevue rxjava
  58. Bugatti Chiron maintenance cost exposure! One piece for one car, burn money endlessly
  59. android应用开发基础答案,深入理解Nginx
  60. 做了三年前端,你才知道10分钟就能实现一个PC版魔方游戏