Jak wyeksportować wiele modułów ES6 z jednego pakietu NPM

16

Zbudowałem stosunkowo niewielki pakiet NPM składający się z około 5 różnych klas ES6 zawartych w jednym pliku, wszystkie wyglądają mniej więcej tak:

export default class MyClass {
    // ...
}

Następnie skonfigurowałem punkt wejścia dla mojego pakietu, który wygląda następująco:

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

Następnie uruchomiłem punkt wejścia przez webpack i babel, kończąc na transpilowanym i zminimalizowanym pliku index.js

Instalowanie i importowanie pakietu działa dobrze, ale gdy wykonam następujące czynności z kodu mojego klienta:

import { MyClass } from 'my-package';

Nie tylko importuje „MyClass”, ale importuje cały plik, w tym wszystkie zależności każdej klasy (niektóre z moich klas mają ogromne zależności).

Doszedłem do wniosku, że tak działa webpack, gdy próbujesz zaimportować części już dołączonego pakietu? Skonfigurowałem więc moją lokalną konfigurację webpacka, aby działała również node_modules/my-packageprzez Babel, a następnie spróbowałem:

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

Ale nawet to importuje każdą pojedynczą klasę eksportowaną przez index.js. Jedyne, co wydaje się działać tak, jak chcę, to jeśli:

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

Ale wolałbym:

  1. Mogę zaimportować transpilowany i zminimalizowany plik, aby nie musiałem mówić webpackowi, aby uruchomił babel wewnątrz node_modules i
  2. Mogę importować każdą indywidualną klasę bezpośrednio z mojego punktu wejścia zamiast konieczności wprowadzania ścieżki do każdego pliku

Jaka jest tutaj najlepsza praktyka? Jak inni osiągają podobne ustawienia? Zauważyłem, że GlideJS ma wersję pakietu ESM, która pozwala importować tylko to, czego potrzebujesz, bez konieczności uruchamiania na przykład babel.

Pakiet, o którym mowa: 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']
                        }
                    }
                ]
            }
        ]
    }
};

pakiet.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"
  }
}
powerbuoy
źródło
1
Czy dodałeś mainatrybut (punkt wejścia) do pliku.jpg w bibliotece lib? Sprawdź swoją wersję. Jak pakujesz swój pakiet lib?
Abhishek
Główną właściwością pliku package.json jest kierunek do punktu wejścia do modułu opisanego przez pakiet.json. W aplikacji Node.js, gdy moduł jest wywoływany za pomocą instrukcji request, eksport modułu z pliku o nazwie głównej właściwości będzie tym, co zostanie zwrócone do aplikacji Node.js.
Abhishek
Tak, główna właściwość wskazuje mój index.js, który eksportuje wszystkie pozostałe klasy. Pakuję plik main / index.js za pomocą webpack i babel. Wszystko to wyjaśniono w pytaniu.
powerbuoy
to może ci pomóc - danielberndt.net/blog/2018/…
Abhishek
Możesz również przy ich implementacji kompilacji - github.com/mui-org/material-ui/blob/master/packages/material-ui/... Aby mieć mniejszy rozmiar kompilacji, lepiej to zrobić import { MyClass } from 'my-package/src/MyClass';. Możesz także usunąć pakiet kompilacji src, aby skrócić ścieżkę pliku.
Abhishek

Odpowiedzi:

1

Doszedłem do wniosku, że tak działa webpack, gdy próbujesz zaimportować części już dołączonego pakietu?

Tak, sposób, w jaki go skonfigurowałeś, polega na importowaniu każdej klasy z pliku index.js, który jest następnie transponowany do jednego pliku (jeśli jest przeznaczony na ES5, co jest najczęstsze *). Oznacza to, że po zaimportowaniu tego pliku do innego pliku, pojawia się w całości wraz ze wszystkimi tymi klasami.

Jeśli chcesz odpowiednio wstrząsnąć drzewem, powinieneś unikać transpilowania go do pakietu CommonJS (ES5). Sugeruję, aby zachować moduły ES6, same lub w innym miejscu niż pakiet ES5. Ten artykuł powinien pomóc Ci w pełni to zrozumieć i zawiera zalecane instrukcje. Zasadniczo sprowadza się to do ustawienia środowiska Babel za pomocą preset-env (wysoce zalecane, jeśli jeszcze go nie używasz!), Aby zachować składnię ES6 . Oto odpowiednia konfiguracja Babel, jeśli nie chcesz transponować na ES5:

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

W artykule szczegółowo opisano, jak skonfigurować 2 pakiety, z których każdy używa innej składni modułu.

Warto również zauważyć, i jest również wspomniany w artykule, możesz ustawić punkt wejścia modułu ES w pakiecie.json. To informuje Webpack / Babel, gdzie można znaleźć moduły ES6, co może być wszystkim, czego potrzebujesz w twoim przypadku użycia. Wydaje się, że konwencjonalna mądrość mówi:

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

Ale dokumentacja węzła ma następującą postać:

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

Gdybym miał czas, pobawiłbym się tym i sprawdził, który działa poprawnie, ale to powinno wystarczyć, aby ustawić właściwą ścieżkę.


* Pakiety ukierunkowane na ES5 są w formacie CommonJS, który musi zawierać wszystkie powiązane pliki, ponieważ ES5 nie ma natywnej obsługi modułów. Tak było w ES2015 / ES6.

CaitlinWeb
źródło
Próbowałem dodać targets.esmodules: truei chociaż wprowadziłem zmiany do wbudowanego skryptu, nie zmieniło to, co ostatecznie zostało zaimportowane. Import jednej klasy z my-packagenadal importuje wszystko. Próbowałem też zmian w package.json(wraz z inną zmianą) i to też niczego nie zmieniło. Cóż, dodanie type: modulefaktycznie zepsuło moją kompilację z „Musi użyć importu, aby załadować moduł ES: /sleek-ui/webpack.config.js wymaga () modułów ES nie jest obsługiwane”. więc musiałem to usunąć. Rzucę okiem na linkowany artykuł.
powerbuoy
Ok, więc artykuł tak naprawdę kazał mi ustawić modules: false(nie w środku targets), ale to też nie zadziałało ... Myślę, że po prostu zaimportuję bezpośrednio z pliku źródłowego i będę dalej uruchamiał babel przez node_modules, dopóki nie będziemy mogli używać tych rzeczy natywnie.
powerbuoy
@ powerbuoy Działa importowanie z pliku źródłowego. Może nie było to jasne z mojego postu, a jeśli tak, mogę go edytować, ale chcesz zaimportować tylko klasę import MyClass from 'my-package/myClass';. Dobrym przykładem tego jest repozytorium .
CaitlinWeb
-1

To jest ważny przypadek użycia. Ostatecznym celem jest to zrobić, import { MyClass } from 'my-package'ale istnieje czystszy sposób na zrobienie tego.

Utwórz plik indeksu agregatora w swoim my-package. Zasadniczo my-package/index.jsi powinno to wyglądać tak:

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

export { MyClass, MyOtherClass }

To możesz zrobić import { MyClass } from 'my-package'. Bułka z masłem.

Baw się dobrze!

idancali
źródło
Właśnie to właśnie robię, co moim zdaniem było całkiem jasne w pytaniu.
powerbuoy