Qu'est-ce que MagicString et ces lignes sont-elles si magiques?

MagicString est une bibliothèque peu connue. Malgré cela, il résout l'un des problèmes les plus urgents - changer le code source en utilisant sa structure (AST - arbre de syntaxe abstraite).

Dans cet article, nous apprendrons ce qu'est MagicString et si ces lignes sont vraiment «magiques». Cela nous aidera à comprendre le prochain article dans lequel j'expliquerai comment nous avons réussi à traduire la documentation Angular si rapidement, et comment cela aidera à la création d'un traducteur universel pour Markdown et les fichiers de tout autre format.





Il y a 2 semaines, j'ai publié la documentation en langue russe d'Angular ( angular24.ru ). Pendant ce temps, 35 problèmes ont été ajoutés avec des corrections dans le texte et 2 pull request. Je doutais sincèrement que le système dans lequel vous sélectionnez le texte, offrez une traduction et émettez automatiquement sur GitHub fonctionnera. Mais le crowdsourcing fonctionne! :) Vous pouvez en savoir plus à ce sujet dans cet article .

Après la sortie, l'une des questions les plus posées était: «Pourquoi?». La question est absolument correcte, mais pour y répondre, vous devez d'abord comprendre ce qu'est MagicString, comment il fonctionne et comment il est utile.

Supposons que nous ayons un code source simple:

const a = 1;

Nous voulons remplacer const par var . La solution la plus simple consiste à remplacer const par var par le String.prototype.replace habituel . Et pour cette tâche, c'est probablement la solution la plus correcte. Mais que faire si nous devons remplacer const par var uniquement dans la portée globale? Mais ne les remplacez pas à l'intérieur des fonctions? Vous pouvez, bien sûr, proposer une régularité plus complexe ou écrire du code délicat, mais il existe un moyen plus évolutif et flexible.

Nous pouvons utiliser les analyseurs pour obtenir AST - Abstract Syntax Tree. Si vous êtes intéressé par ce qu'est l'AST, rendez-vous sur astexplorer.net . Il s'agit essentiellement d'un arbre qui affiche avec précision la structure de votre code.

De plus, chacun des nœuds de cette AST a un débutet des index de fin indiquant les positions de ces éléments dans le code source. Connaissant ces coordonnées et ayant à portée de main la structure du document, nous pouvons effectuer des remplacements et permutations complexes en préservant la structure du document.



Habituellement, le remplacement se fait à l'aide de la conception de modèle de visiteur et de plusieurs assistants , qui s'enveloppent généralement dans une seule bibliothèque, qui peut être appelée «API de transformateur». Chaque analyseur possède sa propre "API de transformateur".

Ces bibliothèques sont très faciles à utiliser, mais elles ont plusieurs problèmes. L'un d'eux est la performance.

Étant donné que chaque (enfin, presque chaque) nœud de l'arbre AST contient des coordonnées, lors du changement d'un nœud, nous devons souvent mettre à jour les coordonnées du reste de l'arbre. Ici, vous pouvez affirmer que vous pouvez le faire avec un peu de sang - ne mettez pas à jour les coordonnées partout, mais restituez simplement l'AST au texte en fonction de la structure. Mais il y a 1 problème: vous perdrez immédiatement la mise en forme du texte d'origine, ce qui contredit notre tâche - remplacer const par var dans la ligne existante. En fait, nous obtenons une nouvelle ligne avec un nouveau formatage. Et si ce n'est pas un problème pour une petite ligne, imaginez un fichier de 1000 lignes dans lequel la mise en forme a complètement changé suite au remplacement de const par var . Cela ne semble pas très bon.



Et voici la magie de MagicString. J'ai d'abord appris leur existence grâce au projet Rich Harris, appelé Butternut . Le noyer cendré est un minifieur JavaScript. Butternutt aurait été 3 fois plus rapide qu'UglifyJS et 10-15 fois plus rapide que Babili . Je vais continuer et dire que le projet était recouvert d'un bassin en cuivre il y a au moins 3 ans. Mais même alors, j'ai été intrigué par le secret de sa performance. C'était un MagicString.

Jetons un coup d'œil à l'utilisation de MagicString:

var MagicString = require( 'magic-string' );
var s = new MagicString( 'const a = 1; const b = 2;' );

s.overwrite( 0, 5, 'var' );
s.toString(); // 'var a = 1; const b = 2;'

//  

L'algorithme de MagicString est très simple: nous enveloppons la chaîne d'origine dans un objet dans lequel nous n'appliquons pas directement les modifications à la chaîne, mais ajoutons les coordonnées et ce qui doit être fait dans un tableau pour l'avenir. Et seulement lorsque quelqu'un veut obtenir la ligne résultante, nous commençons 1 à 1 pour effectuer les opérations accumulées. Par exemple:

  1. Nous avons remplacé const par var, commençant à l'index 0 et se terminant à l'index 5
  2. Nous savons que tous les remplacements ultérieurs doivent avoir un index inférieur à 2 (var inférieur à const de 2 caractères, une ligne plus courte)
  3. Nous mettons à jour les coordonnées de toutes les opérations
  4. Nous appliquons l'opération suivante, etc.




Tout semble assez simple. Mais pourquoi MagicString est-il plus rapide? La réponse est assez simple: le nombre d'opérations que nous effectuons sur notre arbre est bien inférieur au nombre de nœuds AST. Sans parler de la quantité de mémoire nécessaire pour l'AST et du fait que la traversée d'arbre (se déplaçant à travers un arbre) n'est pas une opération gratuite, mais O (n + m)



Et si je suis prêt à attendre une demi-heure supplémentaire? Et voici le deuxième plus de MagicString. Chaque analyseur invente sa propre API pour la transformation. Et c'est toujours très bien, s'il existe une telle API (tous les analyseurs ne la fournissent pas), très souvent nous nous retrouvons sans la possibilité de remplacer normalement le texte source en utilisant AST. Mais MagicString est une API universelle unique pour changer la chaîne source. Peu importe quel analyseur ou combinaison d'analyseurs vous avez utilisé. Avec MagicString, vous pouvez aussi bien travailler avec n'importe quel AST.



J'espère que MagicString vous intéresse. Dans le prochain article, je parlerai du double MagicString et comment faire un traducteur universel de documents Markdown.

Abonnez-vous à ma chaîne Telegram @obenjiro_notes et Twitter obenjiroafin de ne pas manquer les articles suivants sur le sujet et bien d'autres choses intéressantes.

All Articles