So exportieren Sie mehrere ES6-Module aus einem NPM-Paket


16

Ich habe ein relativ kleines NPM-Paket erstellt, das aus ungefähr 5 verschiedenen ES6-Klassen besteht, die jeweils in einer Datei enthalten sind. Alle sehen ungefähr so ​​aus:

export default class MyClass {
    // ...
}

Ich habe dann einen Einstiegspunkt für mein Paket eingerichtet, der folgendermaßen aussieht:

export { default as MyClass } from './my-class.js';
export { default as MyOtherClass } from './my-other-class.js';

Ich habe dann den Einstiegspunkt durch Webpack und Babel geführt und am Ende eine transpilierte und minimierte index.js erhalten

Das Installieren und Importieren des Pakets funktioniert einwandfrei, aber wenn ich Folgendes aus meinem Client-Code heraus mache:

import { MyClass } from 'my-package';

Es wird nicht nur "MyClass" importiert, sondern die gesamte Datei einschließlich aller Abhängigkeiten jeder Klasse (einige meiner Klassen haben große Abhängigkeiten).

Ich dachte mir, dass Webpack so funktioniert, wenn Sie versuchen, Teile eines bereits gebündelten Pakets zu importieren. Also habe ich meine lokale Webpack-Konfiguration so eingerichtet, dass node_modules/my-packagesie auch babel durchläuft, und dann versucht:

import { MyClass } from 'my-package/src/index.js';

Aber auch dies importiert jede einzelne Klasse, die von index.js exportiert wird. Das einzige, was so zu funktionieren scheint, wie ich es will, ist, wenn ich es tue:

import MyClass from 'my-package/src/my-class.js';

Aber ich würde viel lieber:

  1. Sie können die transpilierte und minimierte Datei importieren, damit ich webpack nicht anweisen muss, babel in node_modules und auszuführen
  2. Sie können jede einzelne Klasse direkt von meinem Einstiegspunkt importieren, anstatt den Pfad zu jeder Datei eingeben zu müssen

Was ist hier die beste Vorgehensweise? Wie erreichen andere ähnliche Setups? Ich habe festgestellt, dass GlideJS eine ESM-Version seines Pakets hat, mit der Sie nur die Dinge importieren können, die Sie benötigen, ohne beispielsweise babel durchlaufen zu müssen.

Das fragliche Paket: https://github.com/powerbuoy/sleek-ui

webpack.config.js

const path = require('path');

module.exports = {
    entry: {
        'sleek-ui': './src/js/sleek-ui.js'
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist'),
        library: 'sleek-ui', // NOTE: Before adding this and libraryTarget I got errors saying "MyClass() is not a constructor" for some reason...
        libraryTarget: 'umd'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: ['@babel/preset-env']
                        }
                    }
                ]
            }
        ]
    }
};

package.json

  "name": "sleek-ui",
  "version": "1.0.0",
  "description": "Lightweight SASS and JS library for common UI elements",
  "main": "dist/sleek-ui.js",
  "sideEffects": false, // NOTE: Added this from Abhishek's article but it changed nothing for me :/
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --mode production"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/powerbuoy/sleek-ui.git"
  },
  "author": "Andreas Lagerkvist",
  "license": "GPL-2.0-or-later",
  "bugs": {
    "url": "https://github.com/powerbuoy/sleek-ui/issues"
  },
  "homepage": "https://github.com/powerbuoy/sleek-ui#readme",
  "devDependencies": {
    "@babel/core": "^7.8.6",
    "@babel/preset-env": "^7.8.6",
    "babel-loader": "^8.0.6",
    "webpack": "^4.42.0",
    "webpack-cli": "^3.3.11"
  },
  "dependencies": {
    "@glidejs/glide": "^3.4.1",
    "normalize.css": "^8.0.1"
  }
}

1
Haben Sie das mainAttribut (Einstiegspunkt) in die package.json Ihrer Bibliothek eingefügt? Checken Sie Ihren Build ein. Und wie bündeln Sie Ihr lib-Paket?
Abhishek

Die Haupteigenschaft von package.json ist eine Richtung zum Einstiegspunkt in das Modul, das in package.json beschrieben wird. Wenn in einer Node.js-Anwendung das Modul über eine require-Anweisung aufgerufen wird, werden die Exporte des Moduls aus der in der main-Eigenschaft genannten Datei an die Node.js-Anwendung zurückgegeben.
Abhishek

Ja, die Haupteigenschaft zeigt auf meine index.js, die alle anderen Klassen exportiert. Ich bündle die Datei main / index.js mit Webpack und Babel. Es ist alles in der Frage erklärt.
Powerbuoy

Dies könnte Ihnen helfen - danielberndt.net/blog/2018/…
Abhishek

Sie können auch bei ihrer Build-Implementierung - github.com/mui-org/material-ui/blob/master/packages/material-ui/…. Um eine kürzere Build-Größe zu haben, ist es besser, dies zu tun import { MyClass } from 'my-package/src/MyClass';. Sie können auch die src-Build-Verpackung entfernen, um den Dateipfad zu verkürzen.
Abhishek

Antworten:


1

Ich dachte mir, dass Webpack so funktioniert, wenn Sie versuchen, Teile eines bereits gebündelten Pakets zu importieren.

Ja, Sie haben es so eingerichtet, dass jede Klasse in index.js importiert wird, die dann in eine Datei transpiliert wird (wenn sie auf ES5 abzielt, was am häufigsten vorkommt *). Dies bedeutet, dass diese Datei beim Import in eine andere Datei vollständig mit all diesen Klassen geliefert wird.

Wenn Sie das Baum richtig schütteln möchten, sollten Sie es nicht in ein CommonJS (ES5) -Bündel umwandeln. Mein Vorschlag ist, die ES6-Module entweder alleine oder an einem vom ES5-Bundle getrennten Ort aufzubewahren. Dieser Artikel soll Ihnen helfen, dies vollständig zu verstehen, und enthält empfohlene Anweisungen. Im Wesentlichen läuft es darauf hinaus, die Babel-Umgebung mit Preset-Env festzulegen (sehr zu empfehlen, wenn Sie es nicht bereits verwenden!), Um die ES6-Syntax beizubehalten . Hier ist die relevante Babel-Konfiguration, wenn Sie nicht auf ES5 transpilieren möchten:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "esmodules": true
        }
      }
    ]
  ]
}

Der Artikel beschreibt, wie Sie zwei Bundles einrichten, die jeweils eine andere Modulsyntax verwenden.

Ebenfalls erwähnenswert und wird auch im Artikel erwähnt. Sie können den Einstiegspunkt für das ES-Modul in package.json festlegen. Dadurch wird Webpack / Babel mitgeteilt, wo sich die ES6-Module befinden. Dies ist möglicherweise alles, was Sie für Ihren Anwendungsfall benötigen. Es scheint, dass konventionelle Weisheit sagt zu tun:

{
  "main": "dist/sleek-ui.js",
  "module": "src/main.js"
}

Die Knotendokumentation lautet jedoch wie folgt:

{
  "type": "module",
  "main": "dist/sleek-ui.js",
  "exports": {
    ".": "dist/sleek-ui.js",
    "./module": "src/main.js"
  }
}

Wenn ich Zeit hätte, würde ich damit herumspielen und sehen, was richtig funktioniert, aber das sollte ausreichen, um Sie auf den richtigen Weg zu bringen.


* ES5-bezogene Bundles haben das CommonJS-Format, das alle zugehörigen Dateien enthalten muss, da ES5 keine native Modulunterstützung bietet. Das kam in ES2015 / ES6.


Ich habe versucht, etwas hinzuzufügen, targets.esmodules: trueund während dies Änderungen am erstellten Skript vorgenommen hat, hat sich nichts daran geändert, was am Ende importiert wurde. Beim Importieren einer einzelnen Klasse aus wird my-packageweiterhin alles importiert. Ich habe auch die Änderungen in package.json(zusammen mit der anderen Änderung) ausprobiert und auch das hat nichts geändert. Nun, das Hinzufügen hat type: modulemeinen Build mit "Muss Import zum Laden des ES-Moduls verwenden: /sleek-ui/webpack.config.js require () von ES-Modulen wird nicht unterstützt." also musste ich das bisschen entfernen. Ich werde mir den verlinkten Artikel ansehen.
Powerbuoy

Ok, der Artikel hat mir tatsächlich gesagt, dass ich setzen soll modules: false(nicht drinnen targets), aber das hat auch nicht funktioniert ... Ich denke, ich werde einfach direkt aus der Quelldatei importieren und babel durch node_modules laufen lassen, bis wir dieses Zeug nativ verwenden können.
Powerbuoy

@powerbuoy Das Importieren aus der Quelldatei funktioniert. Vielleicht war es aus meinem Beitrag nicht klar, und wenn das der Fall ist, kann ich es bearbeiten, aber Sie möchten nur die Klasse wie importieren import MyClass from 'my-package/myClass';. Ein gutes Repo-Beispiel dafür ist lodash-es .
CaitlinWeb

-1

Dies ist ein gültiger Anwendungsfall. Das ultimative Ziel ist es, dies zu tun, import { MyClass } from 'my-package'aber es gibt einen saubereren Weg, dies zu tun.

Erstellen Sie eine Aggregator-Indexdatei in Ihrem my-package. Grundsätzlich my-package/index.jsund es sollte so aussehen:

import MyClass from './my-class.js'
import MyOtherClass from './my-other-class.js'

export { MyClass, MyOtherClass }

Dann können Sie tun import { MyClass } from 'my-package'. Kinderleicht.

Habe Spaß!


Genau das mache ich schon, was ich in der Frage für ziemlich klar hielt.
Powerbuoy
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.