JSX statt h-Funktion (createElement) in Widgets und anderen Bereichen

Hallo, ich experimentiere gerade mit JSX in Widgets, um die Entwicklererfahrung in Themes zu verbessern (ich habe mir mein eigenes Setup für die Theme-Entwicklung ohne header.html usw. ausgedacht; mehr dazu in einem anderen Thema).

Ich frage mich, warum Discourse keine Babel-Plugins innerhalb seiner Codebasis nutzt. In Discourse ist das Babel-Plugin transform-react-jsx verfügbar, das mithilfe der Option jsxPragma JSX in eine benutzerdefinierte createElement-Funktion umwandeln kann. All das ist in Discourse vorhanden.

Das Hinzufügen von transform-react-jsx hier https://github.com/discourse/discourse/blob/master/lib/discourse_js_processor.rb#L143-L149 und das gleichzeitige Freigeben der Babel-Konfiguration, um die jsxPragma hier https://github.com/discourse/discourse/blob/master/vendor/assets/javascripts/babel.js zu ändern, könnte dies ermöglichen.

Ich habe bereits den ersten Teil implementiert; für den Pragma-Teil muss ich noch etwas mehr herausfinden. Leider gibt es keine .babelrc, um individuelle Plugin-Konfigurationen einzurichten. Ich erhalte stattdessen die React.createElement-Funktion.

Mehr zur Verwendung von JSX ohne React hier: Using jsx WITHOUT React | r0b blog

Habt ihr dazu Gedanken oder vielleicht Hilfe, wie dies erreicht werden könnte?

UPDATE:
Ich habe es geschafft, dies zum Laufen zu bringen, indem ich die Pragma-Einstellung in vendor/assets/javascripts/babel.js an Zeile 26586 geändert habe:

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

Hast du das gesehen? Du kannst jetzt Vorlagen verwenden, wenn dir Hyperscript nicht gefällt:

@merefield Ja, ich kenne hbs, aber ich bin kein „typischer Discourse-Themenentwickler

Letztendlich liegt die Entscheidung bei @eviltrout. Ich kann dir aber mein Gefühl dazu sagen. Ja, JSX kann angenehm zu verwenden sein und wir könnten es in Discourse einbauen, aber ich denke nicht, dass wir das tun werden, da wir es unterstützen müssten und wir eher dazu neigen, Widgets irgendwann zu entfernen, als weitere Optionen hinzuzufügen.

Ich verstehe deinen Punkt vollkommen. Ich frage mich nur, warum man die Konfiguration für Babel (und eine Reihe anderer Dinge) nicht für Theme- und Plugin-Entwickler öffnet?

Zum Beispiel könnte eine .babelrc im Theme-Ordner für zusätzliche Optionen des JS-Transpilers verwendet werden. Wenn die .babelrc vorhanden ist, kann discourse_js_processor sie nutzen, die Babel-Konfiguration erweitern und eine Reihe zusätzlicher JS-Funktionen aktivieren.

Nur eine Idee.

Es geht nur darum, eine ‘öffentliche API’ im weitesten Sinne zu verstehen: Alles, was wir zulassen, müssen wir auch langfristig unterstützen. Was sich heute einfach zu warten scheint, könnte morgen bereits schwierig werden. Wir experimentieren derzeit definitiv mit vielen Dingen in der Pipeline und im Client-Teil der App. Mal sehen, was Robin dazu denkt.

Bitte fühle dich nicht unter Druck gesetzt, ich bin nur ein neugieriger Entwickler mit ein paar Fragen. Ich komme aus der Full-Stack-JS-Welt (Node/Vue), wo Ember einfach keine Option ist, besonders wenn man React oder noch besser Vue hat.

Ember fühlt sich so unnatürlich an, und handlebars ist mit seinen Funktionen einfach veraltet. Die Template-Möglichkeiten von JSX und insbesondere von Vue SFC können nicht mit etwas so Einfachem wie hbs verglichen werden.

Ich stimme nicht zu, dass Ember keine Option für Full-Stack-JS ist. Ich verstehe das Argument, dass Ember nicht so populär ist wie andere Frameworks und man sich aus diesem Grund vielleicht für ein anderes Framework entscheiden möchte, aber es gibt sicherlich viele, viele Seiten, die Node im Backend und Ember im Frontend verwenden. Schließlich basiert Embers gesamte Tooling auf Node.

Das gesagt, JSX ist nicht in der Zukunft von Discourse Core vorgesehen, aber wenn es Dinge gibt, die wir tun könnten, um seine Nutzung in Plugins zu ermöglichen, bin ich dafür offen. Bitte erstelle einen Vorschlag, wie das funktionieren würde!

Es ist eine Option, aber in meiner Entwicklung und im Umfeld darum herum ist es das nicht, und ich persönlich würde es nicht verwenden, da Vue sich für diese Art von Anwendungen wie eine natürliche Wahl anfühlt.

Nur etwas Kontext:

In den letzten Monaten habe ich an der Entwicklung eines ziemlich benutzerdefinierten Themes für einen Kunden gearbeitet, bei dem ich eine Menge über die Funktionsweise von Discourse gelernt habe, aber das ist wahrscheinlich nur ein kleiner Prozentsatz.

Derzeit refaktoriere ich eine Reihe von Anfängerfehlern und versuche, die Dinge so benutzerfreundlich wie möglich für zukünftige Kollegen zu gestalten, die meine Arbeit möglicherweise warten müssten. Deshalb habe ich ein wenig mit JSX experimentiert.

Heute habe ich den Ansatz aus dem ersten Beitrag des Themas ausprobiert. Ich habe transform-react-jsx hier hinzugefügt: 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

Und habe die Pragma-Einstellung in vendor/assets/javascripts/babel.js an Zeile 26586 geändert:

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

Das hat es mir ermöglicht, die h-Funktion aus virtual-dom anstelle von React.createElement im durch Babel transformierten Code zu verwenden. h ist Teil von Discourse und das fühlte sich wie eine natürliche Wahl an.

Ich kenne die Geschichte der Discourse-Entwicklung nicht, und Sie hatten wahrscheinlich Ihre Gründe, eine vollständige babel.js-Datei mit einer Reihe nützlicher Plugins in den Code aufzunehmen. Es ist schade, dass diese Plugins nicht für Endentwickler wie mich verfügbar sind. Mein Vorschlag wäre daher:

  1. Verwenden Sie eine Art Babel-Konfiguration, z. B. .babelrc, .babel.yml, oder definieren Sie in about.json ein Babel-Konfigurationsfeld wie in package.json Configure Babel · Babel. Diese Konfiguration könnte auf alle diese Plugins in babel.js verweisen (oder vielleicht auf andere, siehe 3.).

  2. In discourse_js_processor.rb#babel_parse https://github.com/discourse/discourse/blob/master/lib/discourse_js_processor.rb#L138 diese Konfiguration lesen und sie mit der aktuellen Konfiguration erweitern.

  3. Wenn ein Entwickler ein benutzerdefiniertes Babel-Plugin verwenden möchte, könnte er es in seinem Ordner vendor/assets/javascripts/babel/plugins (oder einfach babel/plugins) hinzufügen, und Discourse würde es erkennen und im Transpilationsprozess verwenden.

Ich bin nicht erfahren in der Rails-Backend-Entwicklung – dies ist also mein Meta-Vorschlag.

Und eine weitere Frage, da wir schon dabei sind: Wie schwierig wäre es für Discourse, die Abhängigkeiten aus z. B. dem package.json von Themes zu lesen, diese beim ersten Start zu installieren und sie im Theme zu verwenden (zu cachen oder ähnlich)? (Wenn sich Abhängigkeiten ändern, prüfen, welche sich geändert haben, und diese installieren oder entfernen usw.).

Nochmals, ihr macht einen großartigen Job und eure Arbeit ist beeindruckend. Bitte fühlt euch von meiner Seite aus unter keinem Druck; ich bin nur hier, um zu lernen, gute Diskussionen zu führen und Meinungen und Ideen auszutauschen. Ich erwarte nicht, dass ihr meine Anfragen oder irgendetwas anderes umsetzt.

Was meinst du damit? Was ist mit Handlebars und Ember nicht möglich, das in einem anderen Framework implementiert ist?

Was ich meine: JSX ist JavaScript, keine Template-Sprache. Da JSX selbst JavaScript ist, ist es mächtiger (zum Beispiel: Ternäroperator, map-Funktion usw.). Man kann in Ember/Handlebars zwar alles Mögliche umsetzen, aber JSX ist meiner Meinung nach praktischer und entwicklerfreundlicher.

Und hat das funktioniert? Konntest du JSX hinzufügen und war alles in Ordnung?

Zu deinen anderen Fragen, warum Babel so konfiguriert ist: Discourse basiert auf Rails mit der Asset-Pipeline, und die Unterstützung für Webpack wurde erst in Rails-Version 6 hinzugefügt. Der Codebase von Discourse ist 8 Jahre alt! Im Laufe der Zeit haben wir Funktionen hinzugefügt, sind aber mittlerweile an die Grenzen der Architektur gestoßen.

Eines der großen Projekte in diesem Jahr ist die Migration zu Ember CLI. Wenn du dir unsere Commits ansiehst, wirst du sehen, dass bereits Arbeiten in Richtung dieses Ziels in Discourse eingeflossen sind, aber es ist ein langer Weg.

Sobald das umgesetzt ist, wirst du viele weitere JavaScript-Build-Funktionen nutzen können, die derzeit nicht funktionieren, es sei denn, du führst sie innerhalb von Ruby aus, was umständlich ist. In der Zwischenzeit können wir uns nicht die Zeit nehmen, package.json auszulesen und dies automatisch in die Rails-Pipeline zu integrieren.

Ich werde einen POC-PR erstellen, um zu zeigen, wie das funktioniert.

Danke für die Einblicke und die Erklärung!

Ein Update – Ich habe es geschafft, ein ThemeField-Babel zu erstellen, das verwendet wird, um babel.config.json aus dem Theme zu lesen und Plugins in Babel.transform im discourse_js_processor einzufügen.

Ich hoffe, den PR bald als Proof of Concept vorzustellen. Eine Warnung – Es wird ein ziemliches Chaos sein :sweat_smile:.

PS. Ruby ist so einfach zu verwenden :star_struck:

Hier ist es: