Unterstützung für ES6-Importe im ES5-Modul

8

Für meine Schüler im ersten Jahr habe ich eine einfache ES5-basierte Bibliothek bereitgestellt, die mit dem Revealing Module Pattern geschrieben wurde. Hier ist ein Ausschnitt aus dem "Haupt" -Modul / Namespace, der andere Erweiterungen enthalten wird:

window.Library = (function ($) {
    if (!$) {
        alert("The Library is dependent on jQuery, which is not loaded!");
    }

    return {};
})(window.jQuery);

Dies funktioniert für fast 99,9% der Studenten, die neu in der Webentwicklung sind und keine ausgefallenen Dinge wie ES6 in Kombination mit Webpack oder Babel verwenden.

Die 0,1% haben mich nun gebeten, eine ES6-basierte Version bereitzustellen, die ordnungsgemäß importiert werden kann. Ich würde das gerne zur Verfügung stellen, aber ich bin mir nicht sicher, wie ich das am besten angehen soll.

Ich möchte natürlich den ES5-Weg beibehalten, damit meine Schüler die Datei einfach mit einem Skript-Tag einfügen und eingeben können, Library.SomeExtension.aFunction();wo immer sie möchten. Darüber hinaus sind einige der Erweiterungen auf jQuery angewiesen, das auf ähnliche Weise wie das obige Snippet injiziert wird.

Ich suche jetzt nach einem wartbaren Weg, um das Beste aus beiden Welten mit einer Codebasis und jQuery als Abhängigkeit herauszuholen. Ich möchte 99,9% a geben window.Library, während ich 0,1% eine Verwendungsmöglichkeit geben möchte import Library from 'library'.

Kann ich dies mit einer einzelnen JS-Datei erreichen, die beides kann? Oder würde ich eine spezielle ES6-Version benötigen (kein Problem)? Und vor allem: Wie würde ich meinen Code (alle ähnlich dem obigen Snippet) so neu mischen, dass ich beide Situationen unterstützen kann?

Alle Hinweise werden sehr geschätzt!

EDIT: Nur als Randnotiz, ich habe bereits eine vorhanden gulpfile.js, die diese Bibliothek durchläuft Babel, minifiersund andere Dinge. Das zu erweitern, um das oben genannte Problem zu lösen, ist also kein Problem!

Lennard Fonteijn
quelle
Wahrscheinlich am besten mit ES6 selbst und einem Transpiler / Bundler, der das globale Enthüllungsmodul generiert. Dann bieten Sie Ihr Quellmodul den 0,1% an. Also ja, eine einzelne Quelldatei, aber zwei verteilte Dateien.
Bergi
Ich hoffe auf eine Antwort auf Kann ich dies mit einer einzigen JS-Datei erreichen, die beides kann? - Wenn dies möglich ist, was möglicherweise nicht der Fall ist, wie würde das Skript aussehen?
Schnee
@Snow Derzeit wird mit den in den Antworten angegebenen Ressourcen herumgespielt. Ich muss ein bisschen mehr Zeit damit verbringen, etwas zu bekommen, mit dem ich zufrieden bin, aber es scheint möglich zu sein, wenn Sie ein bisschen kreativ beim Erstellen Ihrer Skripte sind! In meinem speziellen Fall möchte ich das Library.SubLibrary.functionNameSetup wirklich und auch in separaten Dateien behalten , selbst wenn jemand es als ES6 oder was auch immer importiert. (Dies liegt hauptsächlich daran, dass Erweiterungen sich auch gegenseitig verwenden können. Bei Verwendung eines Standard-ES6-Setups würde dies zu Zirkelverweisen führen.)
Lennard Fonteijn
Ja, es ist trivial, dies mit separaten Dateien zu erreichen: eine Verwendung exporthaben und die andere zuweisen window. Ich dachte, es wäre großartig, wenn beide in einer einzigen Datei ausgeführt werden könnten, da sich das so viel eleganter anfühlt, aber das exportSchlüsselwort scheint nur in a zu funktionieren type=module. Ich weiß nicht, ob es eine Problemumgehung gibt oder ob es einfach unmöglich ist.
Schnee
@LennardFonteijn Zirkuläre Abhängigkeiten funktionieren mit ES6-Modulen einwandfrei. Stellen Sie lediglich sicher, dass Sie nur exportierte Funktionen deklarieren und keinen Initialisierungscode (der von der Reihenfolge der Modulbewertung abhängt) auf der obersten Ebene ausführen. Ich würde empfehlen, keine verschachtelten Namespace-Objekte zu verwenden, was in ES6 ziemlich unidiomatisch ist.
Bergi

Antworten:

5

Nachdem ich ein paar Nächte lang Code verschoben und so viele Gulp-Pakete installiert hatte, dass ich mich nicht mehr an alle erinnere, die ich ausprobiert habe, entschied ich mich schließlich für etwas, mit dem ich halb zufrieden bin.

Um zunächst meine eigene Frage zu beantworten, die auch von @Bergi in den Kommentaren unterstützt wurde:

Kurze Antwort: Nein, Sie können die ES6-Syntax in keiner Weise (derzeit) mit ES5 (Modulen) in einer einzigen Datei mischen, da der Browser bei der Verwendung von Fehler macht export.

Lange Antwort: Technisch gesehen können Sie Ihren Skriptelementtyp auf "Modul" setzen, wodurch Browser das exportSchlüsselwort akzeptieren . Ihr Code wird jedoch einfach zweimal ausgeführt (einmal beim Laden und einmal nach dem Import). Wenn du damit leben kannst, wird das Nein ein Ja, irgendwie.

Was habe ich am Ende getan? Lassen Sie mich ganz klar sagen, dass ich das IIFE-Modul-Setup, das ich (vorerst) in der ES5-Ausgabedatei hatte, unbedingt beibehalten wollte. Ich habe meine Codebasis auf reines ES6 aktualisiert und alle Arten von Kombinationen von Plugins ausprobiert (einschließlich Rollup.js, die von @David Bradshaw erwähnt wurden). Aber ich konnte sie einfach nicht zum Laufen bringen, oder sie würden die Ausgabe vollständig so beschädigen, dass die Browser das Modul nicht mehr laden oder die Sourcemap-Unterstützung einstellen könnten. Mit dem Projekt, das die Studenten gerade zu Ende gehen, habe ich bereits genug meiner Freizeit für die 0,1% meiner Studenten verschwendet. Also viel Glück im nächsten Jahr für eine "schönere" Lösung.

  1. Ich habe mein UMD-Muster (wie von @Sly_cardinal erwähnt) auf jQuery basiert und diesen Code in a geworfen Library.umd.js. Für den ES6-Teil habe ich ein Library.es6.jsmit nur einem export default Librarydrin gemacht.

  2. In beiden Dateien habe ich einen mehrzeiligen Kommentar /*[Library.js]*/eingefügt, in den ich die verkettete, nicht minimierte Library.js einfügen wollte.

  3. Ich habe meine vorhandene Gulp-Aufgabe geändert, um sie nur Library.jsmit concatund zu erstellen, Babelund sie an einem temporären Speicherort gespeichert. Ich habe mich dafür entschieden, die Unterstützung der Quellzuordnung in diesem Prozess zu opfern, da der verkettete Code in der Ausgabe perfekt lesbar war. Technisch gesehen hat die Unterstützung von Quellkarten "funktioniert", aber sie würde den Debugger immer noch zu sehr abwerfen.

  4. Ich habe eine zusätzliche Gulp-Aufgabe hinzugefügt, um den Inhalt der temporären Library.jsDatei zu lesen. Dann habe ich für jede der Dateien aus Schritt 1 den mehrzeiligen Kommentar gefunden und durch den Inhalt ersetzt. Speichern Sie die resultierenden Dateien und werfen Sie sie dann durch das Finale concat(z. B. Bundle mit jQuery) terserund die Generierung der Quellkarte.

Das Endergebnis sind zwei Dateien:

  • Eine mit UMD / ES5-Browser-Unterstützung
  • Eine mit ES6-Unterstützung

Obwohl ich mit dem Ergebnis zufrieden bin, mag ich die Gulp-Task-Kette und den Verlust der Sourcemap-Unterstützung in der ersten Hälfte des Prozesses nicht. Wie bereits erwähnt, habe ich zahlreiche Schluck-Plugins ausprobiert, um den gleichen Effekt zu erzielen (z. B. gulp-inject), aber sie haben entweder nicht richtig funktioniert oder seltsame Dinge an der Quellenkarte vorgenommen (selbst wenn sie behaupteten, sie zu unterstützen).

Für ein nächstes Unterfangen könnte ich etwas anderes als Gulp untersuchen oder mein eigenes Plugin erstellen, das die oben genannten Schritte ausführt, aber richtig: P.

Lennard Fonteijn
quelle
0

Mit Rollup.js können Sie Ihren Code als ES5-Bibliothek sowie als zugehöriges ES6-Modul bündeln.

Rollup verfügt über eine integrierte Unterstützung für Gulp, sodass so etwas ein vernünftiger Ausgangspunkt wäre (basierend auf dem Gulp-Beispiel von Rollup):

    const gulp = require('gulp');
    const rollup = require('rollup');
    const rollupTypescript = require('rollup-plugin-typescript');

    gulp.task('bundle', () => {
      return rollup.rollup({
        input: './src/main.ts',
        plugins: [
          rollupTypescript()
        ]
      }).then(bundle => {
          return Promise.all([
              // Build for ES5
              bundle.write({
                file: './dist/library.js',
                format: 'umd',
                name: 'Library',
                sourcemap: true
            }),

            // Build for ES6 modules
            bundle.write({
                file: './dist/library.esm.js',
                format: 'esm',
                sourcemap: true
            })
          ]);
      });
    });

Weitere Informationen zu den verschiedenen verfügbaren Ausgabeformaten finden Sie unter: https://rollupjs.org/guide/en/#outputformat

Im Allgemeinen ist es am einfachsten, wenn Ihre Bibliotheksquelle mit ES-Modulen geschrieben und dann in das ES5-Bundle gebündelt / transpiliert wird.

Sly_cardinal
quelle
Das sieht toll aus und genau das, was ich brauche! Werde es
versuchen