@babel/helper-environment-visitor
Questa pagina è stata tradotta da PageTurner AI (beta). Non ufficialmente approvata dal progetto. Hai trovato un errore? Segnala problema →
@babel/helper-environment-visitor è un pacchetto di utilità che fornisce un visitatore per il contesto this corrente.
Installazione
- 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
Utilizzo
Per utilizzare il pacchetto nel tuo plugin Babel, importa le funzioni richieste da @babel/helper-environment-visitor:
import environmentVisitor, {
requeueComputedKeyAndDecorators
} from "@babel/helper-environment-visitor";
environmentVisitor
Visita tutti i nodi AST all'interno dello stesso contesto this fino al nodo radice di attraversamento. L'esecuzione di questo visitatore da sola è un'operazione nulla poiché non modifica i nodi AST. Questo visitatore è progettato per essere utilizzato con 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
Rimette in coda la chiave calcolata e i decoratori di un path membro di classe in modo che vengano rivisitati dopo lo svuotamento della coda corrente di attraversamento. Consulta la sezione esempio per ulteriori utilizzi.
if (path.isMethod()) {
requeueComputedKeyAndDecorators(path)
}
Esempio
Sostituire this al livello superiore
Supponiamo di star migrando da JavaScript vanilla a ES Modules. Dato che la parola chiave this è equivalente a undefined al livello superiore di un ESModule (specifica), vogliamo sostituire tutti i this al livello superiore con globalThis:
// replace this expression to `globalThis.foo = "top"`
this.foo = "top";
() => {
// replace
this.foo = "top"
}
Possiamo abbozzare un plugin per la modifica del codice, ecco la nostra prima revisione:
module.exports = (api) => {
const { types: t } = api;
return {
name: "replace-top-level-this",
visitor: {
ThisExpression(path) {
path.replaceWith(t.identifier("globalThis"));
}
}
}
}
La prima revisione funziona per gli esempi visti finora. Tuttavia, non coglie appieno il concetto di livello superiore: ad esempio, non dovremmo sostituire this all'interno di funzioni non freccia come dichiarazioni di funzione, metodi di oggetto e metodi di classe:
function Foo() {
// don't replace
this.foo = "inner";
}
class Bar {
method() {
// don't replace
this.foo = "inner";
}
}
Possiamo saltare l'attraversamento quando incontriamo tali funzioni non freccia. Qui combiniamo più tipi AST con | nel selettore del visitatore.
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|..." è una stringa molto lunga e difficile da mantenere. Possiamo abbreviarla utilizzando 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();
}
}
}
}
}
Il plugin funziona in generale, ma non gestisce un caso limite in cui this al livello superiore viene utilizzato all'interno di elementi di classe calcolati:
class Bar {
// replace
[this.foo = "outer"]() {
// don't replace
this.foo = "inner";
}
}
Ecco un albero sintattico semplificato della sezione evidenziata sopra:
{
"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
}
Se l'intero nodo ClassMethod viene saltato, non potremo visitare this.foo sotto la proprietà key. Tuttavia, dobbiamo visitarlo poiché potrebbe essere un'espressione qualsiasi. Per ottenere ciò, dobbiamo dire a Babel di saltare solo il nodo ClassMethod, ma non la sua chiave calcolata. È qui che requeueComputedKeyAndDecorators diventa 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);
}
}
}
}
}
Manca ancora un caso limite: this può essere utilizzato all'interno di chiavi calcolate di proprietà di classe:
class Bar {
// replace
[this.foo = "outer"] =
// don't replace
this.foo
}
Sebbene requeueComputedKeyAndDecorators possa gestire anche questo caso limite, il plugin è diventato piuttosto complesso, con un tempo significativo dedicato alla gestione del contesto this. Di fatto, la focalizzazione sulla gestione di this ha distolto dal requisito effettivo: sostituire this al livello superiore con globalThis.
environmentVisitor è stato creato per semplificare il codice estraendo la logica di gestione di this soggetta a errori in una funzione helper, evitando di doverla gestire direttamente.
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
]);
}
}
Puoi provare la revisione finale su AST Explorer.
Come suggerisce il nome, requeueComputedKeyAndDecorators supporta anche i decoratori ES:
class Foo {
// replaced to `@globalThis.log`
@(this.log) foo = 1;
}
Dato che le specifiche continuano a evolversi, utilizzare environmentVisitor può essere più semplice che implementare un proprio visitor per il contesto this.
Trovare tutte le chiamate super()
Questo è un frammento di codice da @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,
]);