JSX invece della funzione h (createElement) nei widget e in altri contesti

Ciao, sto sperimentando con JSX nei widget per migliorare l’esperienza degli sviluppatori nei temi (ho creato la mia configurazione per lo sviluppo dei temi senza header.html e simili, ne parlerò più in dettaglio in un altro argomento).

Mi chiedevo perché Discourse non utilizzi i plugin Babel all’interno del proprio codice. In Discourse è disponibile il plugin Babel transform-react-jsx, che può essere usato per trasformare JSX in una funzione createElement personalizzata tramite l’opzione jsxPragma. Tutto questo è già disponibile in Discourse.

Aggiungere transform-react-jsx qui https://github.com/discourse/discourse/blob/master/lib/discourse_js_processor.rb#L143-L149 e in qualche modo esporre la configurazione Babel per modificare il jsxPragma qui https://github.com/discourse/discourse/blob/master/vendor/assets/javascripts/babel.js
potrebbe rendere possibile questa cosa.

Ho già implementato la prima parte; la parte relativa al pragma richiede ancora alcune indagini. Purtroppo, non c’è un file .babelrc per configurare i singoli plugin. Ottengo la funzione React.createElement invece di una funzione personalizzata.

Maggiori informazioni sull’uso di JSX senza React qui: Using jsx WITHOUT React | r0b blog

Qualche idea o magari un aiuto su come si potrebbe realizzare?

AGGIORNAMENTO:
Sono riuscito a farlo funzionare modificando l’impostazione del pragma in vendor/assets/javascripts/babel.js alla riga 26586

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

L’hai visto? Ora puoi usare i Template se non ti piace Hyperscript:

@merefield sì, conosco hbs, ma non sono “uno sviluppatore di temi Discourse tipico” :slight_smile:. Vorrei sfruttare la potenza di JSX, che è vero JavaScript, invece di hbs, che ha funzionalità limitate.

Sono riuscito a utilizzare JSX in un tema Discourse, ma solo nella mia versione forkata di Discourse, quindi non è molto utile per lo sviluppo generale di temi.

Alla fine, la decisione spetta a @eviltrout. Posso però darti la mia opinione. Sì, JSX può essere piacevole da usare e potremmo integrarlo in Discourse, ma non credo che lo faremo, perché dovremmo supportarlo e siamo più orientati a eliminare i widget in futuro piuttosto che aggiungere nuove opzioni.

Capisco perfettamente il tuo punto. Mi chiedo solo: perché non rendere la configurazione di Babel (e di molte altre cose) disponibile per gli sviluppatori di temi e plugin?

Ad esempio, un file .babelrc nella cartella del tema potrebbe essere utilizzato per aggiungere opzioni al transpiler JS. Se .babelrc è presente, discourse_js_processor potrebbe utilizzarlo, estendere la propria configurazione Babel e abilitare numerose funzionalità JS aggiuntive.

È solo un’idea.

È solo una questione di ‘API pubblica’ in senso ampio: tutto ciò che permettiamo, dovremo mantenerlo. Ciò che oggi sembra facile da supportare, domani potrebbe diventare complicato. Stiamo sicuramente sperimentando molte cose nella pipeline e nella parte client dell’app ultimamente. Vediamo cosa ne pensa Robin di tutto questo.

Sentiti pure libero, sono solo uno sviluppatore curioso con alcune domande. Sono arrivato su Discorso dal mondo del full stack JS (Node/Vue), dove Ember non è semplicemente un’opzione, specialmente quando si ha a disposizione React o, meglio ancora, Vue.

Ember sembra così innaturale e handlebars è semplicemente obsoleto nelle sue funzionalità. Il potere di templating di JSX e, in particolare, degli SFC di Vue non può essere confrontato con qualcosa di basilare come hbs.

Non sono d’accordo sul fatto che Ember non sia un’opzione per lo stack JS completo. Comprendo l’argomento secondo cui Ember non è popolare come altri framework e che per questo motivo si potrebbe preferire un altro framework, ma ci sono certamente moltissimi siti che utilizzano Node nel backend e Ember nel frontend. Dopo tutto, tutti gli strumenti di Ember si basano su Node.

Detto questo, JSX non fa parte del futuro di Discourse core, ma se ci sono cose che potremmo fare per abilitarne l’uso nei plugin, sarei aperto all’idea. Per favore, presenta una proposta su come potrebbe funzionare!

È un’opzione, ma nel mio contesto di sviluppo e lavoro non lo è, e personalmente non lo utilizzerei perché Vue sembra una scelta naturale per questo tipo di applicazioni.

Solo un po’ di contesto:

Negli ultimi mesi ho lavorato allo sviluppo di un tema piuttosto personalizzato per un cliente, dove ho imparato molte cose sul funzionamento interno di Discourse, ma probabilmente si tratta solo di una piccola percentuale.

Attualmente, sto rifattorizzando una serie di errori da principiante e sto cercando di rendere le cose il più possibile amichevoli per i futuri colleghi che potrebbero dover mantenere il mio lavoro. Per questo motivo, ho fatto qualche esperimento riguardo a JSX.

Oggi ho provato l’approccio del primo post in questo topic. Ho incluso transform-react-jsx qui: 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 ho modificato l’impostazione del pragma in vendor/assets/javascripts/babel.js alla riga 26586:

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

Questo mi ha permesso di utilizzare la funzione h di virtual-dom invece di React.createElement nel codice trasformato da Babel. h fa parte di Discourse e questo mi è sembrato un adattamento naturale.

Non conosco la storia dello sviluppo di Discourse e probabilmente avevate le vostre ragioni per includere un file babel.js completo con molti plugin utili nel codice. È un peccato che questi plugin non siano disponibili per gli sviluppatori finali come me. La mia proposta sarebbe quindi:

  1. Utilizzare una sorta di configurazione Babel, come .babelrc, .babel.yml, o definire un campo di configurazione Babel in about.json simile a quanto fatto in package.json Configure Babel · Babel. Tale configurazione potrebbe fare riferimento a tutti questi plugin presenti in babel.js (o magari ad altri, vedi punto 3).

  2. In discourse_js_processor.rb#babel_parse https://github.com/discourse/discourse/blob/master/lib/discourse_js_processor.rb#L138 leggere questa configurazione ed estendere quella attuale.

  3. Se uno sviluppatore desidera avere un plugin Babel personalizzato, potrebbe aggiungerlo nella cartella vendor/assets/javascripts/babel/plugins (o semplicemente babel/plugins) e Discourse lo rileverà e lo utilizzerà nel processo di transpilazione.

Non ho esperienza nello sviluppo backend con Rails, quindi questa è la mia proposta meta.

E un’altra domanda, visto che ci siamo: quanto sarebbe difficile per Discourse leggere le dipendenze, diciamo, dal package.json dei temi, installarle alla prima esecuzione e utilizzarle (magari memorizzandole nella cache o qualcosa di simile) all’interno del tema? (Se le dipendenze cambiano, verificare quali sono cambiate e installarle o rimuoverle, ecc.).

Ancora una volta, state facendo un ottimo lavoro e il vostro contributo è incredibile. Per favore, non sentite alcuna pressione da parte mia; sono qui solo per imparare, per una buona discussione e per condividere opinioni e idee. Non mi aspetto che implementiate le mie richieste o altro.

Cosa intendi con questo? Cosa non è possibile fare con Handlebars ed Ember che invece è implementato in un altro framework?

Ciò che volevo dire è che JSX è JavaScript, non un linguaggio di templating. Essendo JavaScript di per sé, è più potente (ad esempio: operatore ternario, funzione map, ecc.). Puoi fare tutto ciò che vuoi in Ember/Handlebars, ma JSX è più comodo e amichevole per gli sviluppatori, a mio avviso.

E ha funzionato? Sei riuscito ad aggiungere JSX e tutto è andato bene?

Ora, per quanto riguarda le altre domande che avevi sul perché Babel è configurato in quel modo: Discourse è costruito su Rails utilizzando l’asset pipeline, e il supporto per webpack è stato aggiunto a Rails solo nella versione 6. La base di codice di Discourse ha 8 anni! Quindi, nel tempo, abbiamo aggiunto funzionalità, ma abbiamo superato i suoi limiti.

Uno dei grandi progetti di quest’anno è la migrazione a Ember CLI. Se osservi i nostri commit, vedrai che stiamo lavorando in questa direzione all’interno di Discourse, ma è un percorso lungo.

Una volta implementato, potrai utilizzare molte più funzionalità di build di JavaScript che attualmente non funzionano a meno che non vengano eseguite all’interno di Ruby, il che è macchinoso. Nel frattempo, non vedo come potremmo dedicare tempo alla lettura di package.json e far sì che venga integrato automaticamente nella pipeline di Rails.

Creerò una PR di proof of concept per dimostrare come funziona.

Grazie per le informazioni e la spiegazione!

Un aggiornamento: sono riuscito a creare un ThemeField per Babel, che viene utilizzato per leggere babel.config.json dal tema e iniettare i plugin in Babel.transform nel discourse_js_processor.

Spero di riuscire a pubblicare la PR presto, solo per mostrarne il proof of concept. Un avvertimento: sarà un gran caos :sweat_smile:.

PS. Ruby è così facile da usare :star_struck:

Ecco: