JSX em vez da função h (createElement) em widgets e outros locais

Oi, tenho experimentado JSX em widgets para melhorar a experiência do desenvolvedor em temas (criei minha própria configuração para desenvolvimento de temas sem header.html etc., falarei mais sobre isso em outro tópico).

Estava me perguntando por que o Discourse não utiliza plugins Babel dentro do seu próprio código. No Discourse, temos disponível o plugin Babel transform-react-jsx, que pode ser usado para transformar JSX em uma função createElement personalizada usando as opções jsxPragma. Tudo isso já está disponível no Discourse.

Adicionar o transform-react-jsx aqui https://github.com/discourse/discourse/blob/master/lib/discourse_js_processor.rb#L143-L149 e, de alguma forma, expor a configuração do Babel para alterar o jsxPragma aqui https://github.com/discourse/discourse/blob/master/vendor/assets/javascripts/babel.js
poderia tornar isso possível.

Já implementei a primeira parte; a parte da pragma ainda precisa de mais investigação. Infelizmente, não há um arquivo .babelrc para configurar plugins individuais. Estou recebendo a função React.createElement em vez de uma função personalizada.

Mais informações sobre o uso de JSX sem React aqui: Using jsx WITHOUT React | r0b blog

Alguma ideia ou talvez ajuda sobre como isso poderia ser alcançado?

ATUALIZAÇÃO:
Consegui fazer isso funcionar alterando a configuração da pragma em vendor/assets/javascripts/babel.js, na linha 26586:

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

Você já viu isso? Agora você pode usar Templates se não gostar do Hyperscript:

@merefield sim, eu sei sobre hbs, mas não sou “o desenvolvedor típico de temas do Discourse” :slight_smile:. Gostaria de usar o poder do JSX, que é JavaScript real, em vez do hbs, que tem funcionalidades limitadas.

E consegui usar JSX em um tema do Discourse agora. Mas foi na minha própria fork do Discourse, então não é muito útil para o desenvolvimento geral de temas.

No final, a decisão é do @eviltrout. Posso, no entanto, compartilhar minha opinião. Sim, o JSX pode ser agradável de usar e poderíamos incluí-lo no Discourse, mas não acho que o faremos, pois teríamos que dar suporte a ele e estamos mais inclinados a eliminar os widgets em algum momento do que a adicionar mais opções.

Eu entendo completamente seu ponto. Estou apenas me perguntando, por que não abrir a configuração do Babel (e de um monte de outras coisas) para desenvolvedores de temas e plugins?

Por exemplo, o arquivo .babelrc na pasta do tema pode ser usado para algumas opções adicionais do transpilador de JS. Se o .babelrc estiver presente, o discourse_js_processor pode usá-lo, estender sua configuração do Babel e habilitar um monte de recursos adicionais de JS.

Apenas uma ideia.

É apenas uma questão de ‘API pública’ em um sentido amplo: tudo o que permitirmos, teremos que dar suporte. O que parece fácil de manter hoje pode se tornar difícil amanhã. Estamos definitivamente experimentando muitas coisas no pipeline e na parte do lado do cliente do aplicativo ultimamente. Vamos ver o que o Robin acha de tudo isso.

Por favor, não sinta pressão; sou apenas um desenvolvedor curioso com algumas perguntas. Cheguei ao Discourse vindo do mundo full stack JS (Node/Vue), onde o Ember simplesmente não é uma opção, especialmente quando você tem o React ou, melhor ainda, o Vue.

O Ember parece tão pouco natural, e os handlebars estão simplesmente desatualizados em termos de funcionalidades. O poder de template do JSX e, especialmente, dos Vue SFC, não pode ser comparado com algo básico como hbs.

Eu discordo de que Ember não seja uma opção para full stack JS. Entendo o argumento de que o Ember não é tão popular quanto outros frameworks e que você pode querer escolher outro framework por esse motivo, mas certamente existem muitos e muitos sites usando Node no back-end e Ember no front-end. Afinal, toda a ferramenta do Ember é baseada no Node.

Dito isso, JSX não faz parte do futuro do núcleo do Discourse, mas se houver algo que possamos fazer para permitir seu uso em plugins, estou aberto a isso. Por favor, faça uma proposta de como isso funcionaria!

É uma opção, mas no meu desenvolvimento e no trabalho relacionado a ele, não é. E eu pessoalmente não o usaria, pois o Vue parece ser uma escolha natural para esse tipo de aplicação.

Apenas um pouco de contexto:

Nos últimos meses, tenho trabalhado no desenvolvimento de um tema bastante personalizado para um cliente, onde aprendi várias coisas sobre o funcionamento interno do Discourse, mas isso provavelmente representa apenas uma pequena porcentagem.

Atualmente, estou refatorando vários erros de iniciante e tentando tornar as coisas o mais amigável possível para futuros colegas que possam acabar mantendo meu trabalho. Por isso, fiz algumas experimentações em relação ao JSX.

Hoje, tentei a abordagem do primeiro post do tópico. Incluí transform-react-jsx aqui: 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

E alterei a configuração de pragma em vendor/assets/javascripts/babel.js na linha 26586:

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

Isso me permitiu usar a função h do virtual-dom em vez de React.createElement no código processado pelo Babel. A função h faz parte do Discourse e isso pareceu uma escolha natural.

Não conheço a história do desenvolvimento do Discourse e vocês provavelmente tiveram suas razões para adicionar o arquivo completo do babel.js com vários plugins úteis no código. É uma pena que esses plugins não estejam disponíveis para o desenvolvedor final, como eu. Minha proposta seria:

  1. Usar algum tipo de configuração do Babel, como .babelrc, .babel.yml, ou definir um campo de configuração do Babel no about.json, semelhante ao que é feito no package.json (Configure Babel · Babel). Essa configuração poderia referenciar todos esses plugins no babel.js (ou talvez outros, veja o item 3).

  2. Em discourse_js_processor.rb#babel_parse (https://github.com/discourse/discourse/blob/master/lib/discourse_js_processor.rb#L138), ler essa configuração e estendê-la à configuração atual.

  3. Se um desenvolvedor quiser ter um plugin personalizado do Babel, ele poderia adicioná-lo à pasta vendor/assets/javascripts/babel/plugins (ou apenas babel/plugins) e o Discourse o detectaria e o utilizaria no processo de transpilação.

Não tenho experiência no desenvolvimento do backend em Rails, então esta é minha proposta meta.

E outra pergunta, já que estamos nisso: quão difícil seria para o Discourse ler as dependências de, por exemplo, o package.json de temas, instalá-las na primeira execução e usá-las (mantendo-as em cache ou algo assim) dentro do tema? (Se as dependências mudarem, verificar quais mudaram e instalá-las ou removê-las, etc.).

Mais uma vez, vocês estão fazendo um ótimo trabalho e seu trabalho é incrível. Por favor, não sintam nenhuma pressão da minha parte; estou aqui apenas para aprender, participar de boas discussões e compartilhar opiniões e ideias. Não espero que vocês implementem minhas solicitações ou nada disso.

O que você quer dizer com isso? O que não é possível fazer com Handlebars e Ember que já foi implementado em outro framework?

O que quis dizer é que o JSX é JavaScript, não uma linguagem de template. Por ser JavaScript puro, ele é mais poderoso (por exemplo: operador ternário, função map, etc.). Você pode fazer o que quiser no Ember/Handlebars, mas o JSX é mais conveniente e amigável para desenvolvedores, na minha opinião.

E isso funcionou? Você conseguiu adicionar JSX e tudo ficou OK?

Agora, sobre as outras perguntas que você fez sobre por que o Babel está configurado dessa forma: o Discourse é construído sobre o Rails usando o pipeline de ativos, e o suporte ao webpack foi adicionado ao Rails apenas na versão 6. A base de código do Discourse tem 8 anos! Com o tempo, adicionamos coisas a ela, mas superamos suas limitações.

Um dos grandes projetos deste ano é a migração para o Ember CLI — se você observar nossos commits, verá trabalhos em direção a esse objetivo sendo integrados ao Discourse, mas é um caminho longo.

Uma vez que isso esteja em vigor, você poderá usar muitos mais recursos de construção de JavaScript que atualmente não funcionam, a menos que sejam executados dentro do Ruby, o que é trabalhoso. Enquanto isso, não vejo a possibilidade de dedicarmos tempo à leitura do package.json e fazer com que isso seja integrado automaticamente ao pipeline do Rails.

Vou criar um PR de POC para demonstrar como isso funciona.

Obrigado pela visão e explicação!

Uma atualização — Consegui criar um ThemeField para o Babel, que é usado para ler o babel.config.json do tema e injetar plugins no Babel.transform dentro do discourse_js_processor.

Espero conseguir enviar o PR em breve, apenas para mostrar um proof of concept. Um aviso: vai ser uma bagunça enorme :sweat_smile:.

PS: Ruby é tão fácil de trabalhar :star_struck:

Aqui está: