Не удалось разобрать декоратор метода класса в плагине Babel

#babeljs #typescript-decorator

Вопрос:

Я пишу плагин Babel, который управляет узлом AST, связанным с конкретным декоратором. Я пересекаю AST, но по какой — то причине мой плагин не обнаруживает декоратор метода- node.decorators всегда равен нулю, когда посетитель посещает узел.

Это плагин:

 import { ClassMethod, Decorator } from '@babel/types';
import { get } from 'lodash';
import { NodePath, PluginObj } from '@babel/core';

const providerArgumentsTransformer = (): PluginObj => ({
  visitor: {
    ClassMethod({ node, parent }: NodePath<ClassMethod>) {
      const decorator = getProviderDecorator(node.decorators); // <- node.decorators is always null
      if (getDecoratorName(decorator) === 'Provides') {
        console.log('Success');
      }
    },
  },
});

function getProviderDecorator(decorators: Array<Decorator> | undefined | null): Decorator | undefined {
  return decorators?.find((decorator) => get(decorator, 'expression.callee.name') === 'Provides');
}

function getDecoratorName(decorator?: Decorator): string | undefined {
  return get(decorator, 'expression.callee.name');
}

export default providerArgumentsTransformer;
 

Я тестирую декоратора следующим образом:

 import { PluginObj } from '@babel/core';
import * as babel from '@babel/core';
import providerArgumentsTransformer from './providerArgumentsTransformer';

const code = `class MainGraph {
  Provides(clazz, propertyKey, descriptor) { }

  @Provides()
  someString(stringProvider) {
    return stringProvider.theString;
  }
}`;

describe('Provider Arguments Transformer', () => {
  const uut: PluginObj = providerArgumentsTransformer();

  it('Exposes transformer', () => {
    babel.transformSync(code, {
      plugins: [
        ['@babel/plugin-proposal-decorators', { legacy: true }],
        ['@babel/plugin-proposal-class-properties', { legacy: true }],
        [uut, { legacy: true }],
      ],
      configFile: false,
    });
  });
});
 

Интересно, связана ли проблема с тем, как babel.transformSync используется, или, возможно, посетитель настроен неправильно.

Ответ №1:

Оказывается, декораторы отсутствовали, потому что @babel/plugin-proposal-декораторы очищают декораторов, когда они пересекают AST.

Чтобы посетить узел раньше @babel/plugin-proposal-decorators , мне пришлось немного изменить своего посетителя. Этот подход, вероятно, следует оптимизировать, посетив ClassBody или ClassExpression вместо Program этого .

 const providerArgumentsTransformer: PluginObj = {
  visitor: {
    Program(path: NodePath<Program>) {
      path.traverse(internalVisitor);
    },
  },
};

const internalVisitor = {
  ClassMethod: {
    enter({ node }: NodePath<ClassMethod>) {
      // node.decorators are not null anymore
    },
  },
};