JSX вместо функции h (createElement) в виджетах и других местах

Привет! Я экспериментирую с JSX в виджетах для улучшения опыта разработки в темах (я создал собственную настройку для разработки тем без header.html и т. д., подробнее об этом в другой теме).

Меня интересует, почему Discourse не использует плагины Babel в своей кодовой базе. В Discourse доступен плагин transform-react-jsx, который можно использовать для преобразования JSX в пользовательскую функцию createElement с помощью опции jsxPragma. Всё это уже есть в Discourse.

Добавление transform-react-jsx здесь https://github.com/discourse/discourse/blob/master/lib/discourse_js_processor.rb#L143-L149 и каким-то образом предоставление конфигурации Babel для изменения jsxPragma здесь https://github.com/discourse/discourse/blob/master/vendor/assets/javascripts/babel.js
могло бы сделать это возможным.

Я уже реализовал первую часть, но с частью, касающейся pragma, всё ещё нужно разобраться. К сожалению, нет файла .babelrc для настройки индивидуальных плагинов. В результате я получаю функцию React.createElement вместо пользовательской.

Подробнее об использовании JSX без React здесь: Using jsx WITHOUT React | r0b blog

Есть ли какие-то мысли или, возможно, помощь в том, как это можно реализовать?

ОБНОВЛЕНИЕ:
Мне удалось заставить это работать, изменив настройку pragma в файле vendor/assets/javascripts/babel.js на строке 26586:

var id = state.opts.pragma || "React.createElement";
// на
var id = state.opts.pragma || "h";

Вы видели это? Теперь вы можете использовать шаблоны, если вам не нравится Hyperscript:

@merefield да, я знаю о hbs, но я не «типичный разработчик тем для Discourse» :slight_smile:. Мне хотелось бы использовать мощь JSX, который является настоящим JavaScript, вместо hbs, возможности которого ограничены.

Мне удалось использовать JSX в теме для Discourse, но только в собственном форке Discourse, поэтому это не очень полезно для общей разработки тем.

В конечном счёте решение остаётся за @eviltrout. Я могу лишь поделиться своим мнением. Да, JSX может быть удобен в использовании, и мы могли бы внедрить его в Discourse, однако, я не думаю, что это произойдёт, поскольку нам придётся поддерживать его, а мы скорее склоняемся к тому, чтобы в будущем отказаться от виджетов, чем добавлять новые опции.

Я полностью понимаю вашу точку зрения. Мне просто интересно, почему не открыть конфигурацию для Babel (и множество других вещей) для разработчиков тем и плагинов?

Например, файл .babelrc в папке темы можно использовать для дополнительных опций JS-транспайлера. Если .babelrc присутствует, discourse_js_processor может использовать его, расширить свою конфигурацию Babel и включить множество дополнительных возможностей JS.

Просто идея.

Это просто вопрос «публичного API» в широком смысле: всё, что мы разрешаем, нам придётся поддерживать. То, что сегодня кажется простым в поддержке, завтра может оказаться сложным. В последнее время мы активно экспериментируем с множеством вещей в разработке, особенно с клиентской частью приложения. Посмотрим, что по этому поводу думает Робин.

Не чувствуйте давления, я просто любопытный разработчик с несколькими вопросами. Я пришел в Discourse из мира full-stack JS (Node/Vue), где Ember просто не рассматривается как вариант, особенно когда есть React или, что еще лучше, Vue.

Ember кажется настолько неестественным, а handlebars просто устарели с точки зрения функциональности. Возможности шаблонизации JSX и особенно Vue SFC нельзя сравнивать с чем-то таким базовым, как hbs.

Я не согласен с утверждением, что Ember не подходит для full-stack JavaScript. Я понимаю аргумент о том, что Ember менее популярен, чем другие фреймворки, и по этой причине вы можете предпочесть другой фреймворк, но, безусловно, существует множество сайтов, использующих Node на бэкенде и Ember на фронтенде. В конце концов, весь инструментарий Ember основан на Node.

Тем не менее, JSX не входит в планы развития ядра Discourse, но если мы сможем что-то сделать, чтобы разрешить его использование в плагинах, я открыт к этому. Пожалуйста, предложите, как это могло бы работать!

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

Немного контекста:

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

В настоящее время я рефакторю множество ошибок новичка и пытаюсь сделать код максимально удобным для будущих коллег, которые могут заниматься его поддержкой. Поэтому я провел небольшие эксперименты с JSX.

Сегодня я попробовал подход из первого сообщения в теме. Я добавил transform-react-jsx сюда: https://github.com/discourse/discourse/blob/master/lib/discourse_js_processor.rb#L143-L149:

      if opts[:module_name] && !@skip_module
        filename = opts[:filename] || 'unknown'
        "Babel.transform(#{js_source}, { moduleId: '#{opts[:module_name]}', filename: '#{filename}', ast: false, presets: ['es2015'], plugins: [['transform-es2015-modules-amd', {noInterop: true}], 'transform-decorators-legacy', 'transform-react-jsx', exports.WidgetHbsCompiler] }).code"
      else
        "Babel.transform(#{js_source}, { ast: false, plugins: ['check-es2015-constants', 'transform-es2015-arrow-functions', 'transform-es2015-block-scoped-functions', 'transform-es2015-block-scoping', 'transform-es2015-classes', 'transform-es2015-computed-properties', 'transform-es2015-destructuring', 'transform-es2015-duplicate-keys', 'transform-es2015-for-of', 'transform-es2015-function-name', 'transform-es2015-literals', 'transform-es2015-object-super', 'transform-es2015-parameters', 'transform-es2015-shorthand-properties', 'transform-es2015-spread', 'transform-es2015-sticky-regex', 'transform-es2015-template-literals', 'transform-es2015-typeof-symbol', 'transform-es2015-unicode-regex', 'transform-regenerator', 'transform-decorators-legacy', 'transform-react-jsx',exports.WidgetHbsCompiler] }).code"
      end

И изменил настройку прагмы в vendor/assets/javascripts/babel.js на строке 26586:

var id = state.opts.pragma || "React.createElement";
// на
var id = state.opts.pragma || "h";

Это позволило мне использовать функцию h из virtual-dom вместо React.createElement в коде, обработанном Babel. h является частью Discourse, и это казалось естественным выбором.

Я не знаю историю разработки Discourse, и у вас, вероятно, были свои причины для добавления полноценного файла babel.js с набором полезных плагинов в код. Жаль, что эти плагины недоступны конечному разработчику, такому как я. Поэтому мое предложение состоит в следующем:

  1. Использовать какой-либо файл конфигурации Babel: .babelrc, .babel.yml или определить поле конфигурации Babel в about.json, как в package.json (Configure Babel · Babel). Эта конфигурация могла бы ссылаться на все эти плагины в babel.js (или, возможно, на другие, см. пункт 3).

  2. В discourse_js_processor.rb#babel_parse (https://github.com/discourse/discourse/blob/master/lib/discourse_js_processor.rb#L138) читать эту конфигурацию и расширять текущую настройку.

  3. Если разработчик хочет использовать кастомный плагин Babel, он мог бы добавить его в папку vendor/assets/javascripts/babel/plugins (или просто babel/plugins), и Discourse обнаружил бы его и использовал в процессе транспиляции.

Я не имею опыта разработки бэкенда на Rails, поэтому это мое мета-предложение.

И еще один вопрос, пока мы заговорили: насколько сложно было бы заставить Discourse читать зависимости, например, из package.json темы, устанавливать их при первом запуске и использовать (кэшировать или что-то подобное) внутри темы? (Если зависимости изменились, проверьте, какие изменились, установите или удалите их и т. д.).

Еще раз, вы отлично справляетесь, и ваша работа удивительна. Пожалуйста, не чувствуйте никакого давления с моей стороны, я здесь просто для обучения, хорошей дискуссии и обмена мнениями и идеями. Я не ожидаю, что вы реализуете мои запросы или что-либо еще.

Что вы имеете в виду? Что невозможно сделать с помощью Handlebars и Ember, но реализовано в другом фреймворке?

Я имел в виду, что JSX — это JS, а не шаблонный язык. Будучи сам по себе JS, он более мощный (например, тернарный оператор, функция map и т. д.). В Ember/Handlebars можно сделать всё что угодно, но JSX, на мой взгляд, удобнее и дружелюбнее к разработчикам.

И это сработало? Вам удалось добавить JSX, и всё прошло без проблем?

Теперь по поводу других ваших вопросов о том, почему Babel настроен именно так. Discourse создан на Rails с использованием конвейера ассетов, и поддержка webpack была добавлена в Rails только в версии 6. Кодовая база Discourse существует уже 8 лет! Со временем мы добавляли в неё новые возможности, но в итоге она переросла свои ограничения.

Один из крупных проектов этого года — миграция на Ember CLI. Если вы посмотрите наши коммиты, то увидите работу, направленную на достижение этой цели в рамках Discourse, но это долгий путь.

Как только это будет реализовано, вы сможете использовать гораздо больше возможностей сборки JavaScript, которые сейчас не работают, если их не запускать из Ruby, что довольно неудобно. В то же время я не вижу возможности выделить время на чтение package.json и автоматическую интеграцию этого в конвейер Rails.

Я подготовлю черновик PR, чтобы продемонстрировать, как это работает.

Спасибо за ценную информацию и разъяснения!

Обновление — мне удалось создать ThemeField для Babel, который используется для чтения babel.config.json из темы и внедрения плагинов в Babel.transform в discourse_js_processor.

Надеюсь скоро выкатить PR, просто чтобы показать концепцию. Предупреждение — это будет полный хаос :sweat_smile:.

P.S. С Ruby так легко работать :star_struck:

Вот он: