@babel/helper-environment-visitor
Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →
@babel/helper-environment-visitor est un package utilitaire fournissant un visiteur de contexte this actuel.
Installation
- npm
- Yarn
- pnpm
- Bun
npm install @babel/helper-environment-visitor
yarn add @babel/helper-environment-visitor
pnpm add @babel/helper-environment-visitor
bun add @babel/helper-environment-visitor
Utilisation
Pour utiliser ce package dans votre plugin Babel, importez les fonctions nécessaires depuis @babel/helper-environment-visitor :
import environmentVisitor, {
requeueComputedKeyAndDecorators
} from "@babel/helper-environment-visitor";
environmentVisitor
Il visite tous les nœuds AST dans le même contexte this jusqu'au nœud racine de traversée. Exécuté seul, ce visiteur est sans effet car il ne modifie pas les nœuds AST. Il est conçu pour être utilisé avec traverse.visitors.merge.
module.exports = (api) => {
const { types: t, traverse } = api;
return {
name: "collect-await",
visitor: {
Function(path) {
if (path.node.async) {
const awaitExpressions = [];
// Get a list of related await expressions within the async function body
path.traverse(traverse.visitors.merge([
environmentVisitor,
{
AwaitExpression(path) {
awaitExpressions.push(path);
},
ArrowFunctionExpression(path) {
path.skip();
},
}
]))
}
}
}
}
}
requeueComputedKeyAndDecorators
requeueComputedKeyAndDecorators(path: NodePath): void
Replanifie la clé calculée et les décorateurs d'un path de membre de classe pour qu'ils soient revisités après épuisement de la file de traversée actuelle. Consultez la section exemple pour plus d'utilisations.
if (path.isMethod()) {
requeueComputedKeyAndDecorators(path)
}
Exemple
Remplacer this au niveau supérieur
Supposons que nous migrions de JavaScript vanilla vers ES Modules. Puisque le mot-clé this équivaut à undefined au niveau supérieur d'un ESModule (spécification), nous souhaitons remplacer tous les this de niveau supérieur par globalThis :
// replace this expression to `globalThis.foo = "top"`
this.foo = "top";
() => {
// replace
this.foo = "top"
}
Nous pouvons concevoir un plugin de modification de code, voici notre première version :
module.exports = (api) => {
const { types: t } = api;
return {
name: "replace-top-level-this",
visitor: {
ThisExpression(path) {
path.replaceWith(t.identifier("globalThis"));
}
}
}
}
Cette première version fonctionne pour les exemples actuels. Cependant, elle ne capture pas réellement la notion de niveau supérieur : par exemple, nous ne devrions pas remplacer this dans une fonction non-fléchée (déclaration de fonction, méthodes d'objet ou de classe) :
function Foo() {
// don't replace
this.foo = "inner";
}
class Bar {
method() {
// don't replace
this.foo = "inner";
}
}
Nous pouvons ignorer la traversée lorsque nous rencontrons de telles fonctions non-fléchées. Ici nous combinons plusieurs types AST avec | dans le sélecteur de visiteur.
module.exports = (api) => {
const { types: t } = api;
return {
name: "replace-top-level-this",
visitor: {
ThisExpression(path) {
path.replaceWith(t.identifier("globalThis"));
}
"FunctionDeclaration|FunctionExpression|ObjectMethod|ClassMethod|ClassPrivateMethod"(path) {
path.skip();
}
}
}
}
"FunctionDeclaration|..." est une chaîne très longue et difficile à maintenir. Nous pouvons la raccourcir en utilisant l'alias FunctionParent :
module.exports = (api) => {
const { types: t } = api;
return {
name: "replace-top-level-this",
visitor: {
ThisExpression(path) {
path.replaceWith(t.identifier("globalThis"));
}
FunctionParent(path) {
if (!path.isArrowFunctionExpression()) {
path.skip();
}
}
}
}
}
Le plugin fonctionne globalement. Cependant, il ne gère pas un cas limite où this de niveau supérieur est utilisé dans des éléments de classe calculés :
class Bar {
// replace
[this.foo = "outer"]() {
// don't replace
this.foo = "inner";
}
}
Voici un arbre syntaxique simplifié de la section mise en évidence ci-dessus :
{
"type": "ClassMethod", // skipped
"key": { "type": "AssignmentExpression" }, // [this.foo = "outer"]
"body": { "type": "BlockStatement" }, // { this.foo = "inner"; }
"params": [], // should visit too if there are any
"computed": true
}
Si le nœud ClassMethod entier est ignoré, nous ne pourrons pas visiter this.foo sous la propriété key. Pourtant nous devons le visiter car il pourrait s'agir de n'importe quelle expression. Pour y parvenir, nous devons indiquer à Babel d'ignorer seulement le nœud ClassMethod, mais pas sa clé calculée. C'est là qu'requeueComputedKeyAndDecorators devient utile :
import {
requeueComputedKeyAndDecorators
} from "@babel/helper-environment-visitor";
module.exports = (api) => {
const { types: t } = api;
return {
name: "replace-top-level-this",
visitor: {
ThisExpression(path) {
path.replaceWith(t.identifier("globalThis"));
}
FunctionParent(path) {
if (!path.isArrowFunctionExpression()) {
path.skip();
}
if (path.isMethod()) {
requeueComputedKeyAndDecorators(path);
}
}
}
}
}
Il reste un cas limite non couvert : this peut être utilisé dans les clés calculées d'une propriété de classe :
class Bar {
// replace
[this.foo = "outer"] =
// don't replace
this.foo
}
Bien que requeueComputedKeyAndDecorators puisse gérer ce cas également, le plugin est devenu assez complexe à ce stade, avec un temps significatif consacré à la gestion du contexte this. En réalité, cette focalisation sur this a détourné l'attention de l'exigence réelle : remplacer this de niveau supérieur par globalThis.
Le environmentVisitor a été créé pour simplifier le code en extrayant la logique de gestion de this (succesptible d'erreurs) dans une fonction utilitaire, vous évitant ainsi de la traiter directement.
import environmentVisitor from "@babel/helper-environment-visitor";
module.exports = (api) => {
const { types: t, traverse } = api;
return {
name: "replace-top-level-this",
visitor: traverse.visitors.merge([
{
ThisExpression(path) {
path.replaceWith(t.identifier("globalThis"));
}
},
environmentVisitor
]);
}
}
Vous pouvez tester la version finale sur AST Explorer.
Comme son nom l'indique, requeueComputedKeyAndDecorators prend également en charge les décorateurs ES :
class Foo {
// replaced to `@globalThis.log`
@(this.log) foo = 1;
}
Comme la spécification continue d'évoluer, utiliser environmentVisitor peut s'avérer plus simple que d'implémenter votre propre visiteur pour le contexte this.
Trouver tous les appels super()
Voici un extrait de code provenant de @babel/helper-create-class-features-plugin.
const findBareSupers = traverse.visitors.merge<NodePath<t.CallExpression>[]>([
{
Super(path) {
const { node, parentPath } = path;
if (parentPath.isCallExpression({ callee: node })) {
this.push(parentPath);
}
},
},
environmentVisitor,
]);