github.com/codejamninja/react-ast

Разработчик программного обеспечения может написать не так уж много кода в день, но его производительность может возрасти в геометрической прогрессии благодаря метапрограммированию. Согласно интернету, метапрограммирование — это «техника программирования, при которой компьютерная программа обрабатывает другие программы как свои данные». Метапрограммирование отлично подходит для решения повторяющихся проблем.

Например, я публикую много открытого исходного кода. Это означает, что мне приходится снова и снова писать файл readme, руководство, лицензию и журнал изменений. Используя метапрограммирование, я мог создать генератор йоменов, который генерирует эти файлы для меня на основе нескольких входных данных, таких как мое имя, текущий год и название проекта. Именно это я и сделал несколько лет назад со своим генератором-github-проектом.

Генератор yeoman — это, по сути, слащавый шаблонизатор. Механизмы шаблонов — отличный способ решить проблему метапрограммирования, но у них есть серьезный недостаток. Они очень самоуверенны, и их почти невозможно сочинить. Другими словами, они решают вполне конкретную проблему и ничего больше. Если два генератора перекрываются, повторно использовать код между ними практически невозможно.

Другой подход к метапрограммированию, который не имеет этого ограничения, — это манипулирование абстрактным синтаксическим деревом, обычно называемым для краткости AST. AST — это, по сути, древовидное представление исходного кода. Абстрактные синтаксические деревья — мощный инструмент, но с ними трудно работать по своей природе.

AST и механизмы шаблонов часто сталкиваются с другой проблемой в мире метапрограммирования. Код, используемый в этих проектах, имеет тенденцию быстро спагетти. Это make действительно трудно читать и почти невозможно прочитать другому разработчику.

Используя React, я смог создать инструмент, упрощающий взаимодействие с AST. React является компонуемым по своей природе. Это означает, что я могу разбить генератор кода на небольшие модульные компоненты, которые можно использовать во многих проектах. Традиционные методы взаимодействия с AST обычно включают императивное программирование, которое трудно визуализировать и прочитать. Поскольку React является декларативным по своей природе, понимание логики метапрограммирования очень естественно и прекрасно масштабируется между командами.

Идея React AST пришла мне в голову после создания большого генератора кода, взаимодействующего с AST Babel. Я хотел создать привязки TypeScript для GJS, движка времени выполнения JavaScript Gnome. В частности, я хотел создать привязки для интеграции GTK с GJS. Однако задача была непростой. Для этого потребуется около 30 000 строк привязок TypeScript. Очевидным решением было метапрограммирование. Я создал инструмент под названием ts-gir, который генерирует привязки TypeScript из файлов Gir. Инструмент удался. Привязку можно посмотреть ЗДЕСЬ.

Однако, хотя я сэкономил себе месяцы времени, процесс был болезненным. Несмотря на все мои усилия, код в ts-gir спагетировался, и кому-либо будет нелегко внести свой вклад в проект. По иронии судьбы, причина, по которой я создавал привязки TypeScript в первую очередь, заключалась в том, что я мог создавать привязки React к GTK. Я построил доказательство концепции, используя метапрограммирование для react-gtk, но генератор кода почти не поддается сопровождению. Чтобы правильно продвигаться по проекту, мне придется переписать привязки.

Со всеми этими мыслями о React, метапрограммировании, AST и генераторах кода, витающих в моей голове, я неизбежно подумал о создании средства визуализации React для AST. React AST, безусловно, предлагает новый взгляд на метепрограммирование. Это позволит мне продвинуться в моих усилиях по созданию средства визуализации React, которое выполняет рендеринг в GTK.

Ладно, хватит истории. Как это работает на самом деле?

Я включил очень простое руководство ниже, чтобы дать вам представление о том, как будет выглядеть метапрограммирование с React AST.

Сначала необходимо установить модуль npm. Просто запустите следующую команду.

npm install --save react-ast

Создайте файл с именем renderCode.js и поместите содержимое ниже в файл.

import React from 'react';
import {
  ClassDeclaration,
  Code,
  FunctionDeclaration,
  Param,
  ReturnStatement,
  render
} from 'react-ast';
const code = render(
  <>
    <ClassDeclaration name="Hello" superClassName="Array">
      <Code>hello = 'world'</Code>
    </ClassDeclaration>
    <FunctionDeclaration
      name="add"
      params={[<Param key="a">a</Param>, <Param key="b">b</Param>]}
      returnStatement={<ReturnStatement>result</ReturnStatement>}
    >
      <Code>const result=a+b</Code>
    </FunctionDeclaration>
  </>
);
console.log(code);

Убедитесь, что у вас есть установка Babel с проектом.

.babelrc

{
  "presets": [
    [
      "@babel/env",
      {
        "targets": {
          "node": "6"
        }
      }
    ],
    "@babel/react"
  ]
}

Сгенерируйте код, выполнив следующую команду.

babel-node ./renderCode.js

Вы должны увидеть отображаемый код, выведенный на консоль.

class Hello extends Array {
  hello = 'world';
}
function add(a, b) {
  const result = a + b;
  return result;
}

Если вы хотите отображать AST вместо кода, просто используйте функцию renderAst.

import React from 'react';
import {
  renderAst,
  ClassDeclaration
} from 'react-ast';
const ast = renderAst(
  <ClassDeclaration name="Hello" />
);
console.log(ast);

AST будет выглядеть примерно так.

{ type: 'File',
  program:
   { type: 'Program',
     body: [ [Object] ],
     directives: [],
     sourceType: 'script',
     interpreter: null },
  comments: [],
  tokens: [] }

В таком способе генерации кода есть несколько действительно важных вещей. Очень легко читается и понимает, что происходит. Это повышает производительность и снижает вероятность появления ошибок.

Часто автор проекта метапрограммирования выбирает стандарт кодирования генерируемого вывода. Однако, поскольку React AST фактически будет подтверждать кодировку пользователя генератора.

Он также чрезвычайно компонуемый. Например, повторно используемые фрагменты кода можно легко абстрагировать в компонент React AST, который затем можно скомпоновать с другими компонентами React AST.

Сейчас есть только базовые компоненты React AST, такие как ClassDeclaration, FunctionDeclaration и Code. Однако эти базовые компоненты могут быть объединены для создания более сложных компонентов, таких как ReactComponent или ExpressRoute.

Я бы хотел, чтобы больше участников сообщества внесли свой вклад в этот проект. Создание библиотеки повторно используемых и компонуемых компонентов React AST имеет большой потенциал.

Ознакомьтесь с руководством по внесению вклада

Если у вас есть какие-либо вопросы или предложения, вы можете отправить отзыв и сообщить о проблемах по ссылке ниже.

Отправить проблему

github.com/codejamninja/react-ast