Bagaimana cara mengekspor beberapa modul ES6 dari satu paket NPM

16

Saya telah membangun paket NPM yang relatif kecil yang terdiri dari sekitar 5 kelas ES6 berbeda yang terkandung dalam satu file masing-masing, mereka semua terlihat sangat mirip:

export default class MyClass {
    // ...
}

Saya kemudian mengatur titik masuk untuk paket saya yang terlihat seperti ini:

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

Saya kemudian menjalankan entry point melalui webpack dan babel berakhir dengan index.js yang di-transpilasikan dan diperkecil

Menginstal dan mengimpor paket berfungsi dengan baik, tetapi ketika saya melakukan hal berikut dari kode klien saya:

import { MyClass } from 'my-package';

Itu tidak hanya mengimpor "MyClass" itu mengimpor seluruh file termasuk semua dependensi dari setiap kelas (beberapa kelas saya memiliki dependensi yang sangat besar).

Saya pikir ini adalah cara kerja webpack ketika Anda mencoba mengimpor bagian dari paket yang sudah dibundel? Jadi saya mengatur konfigurasi webpack lokal saya untuk dijalankan node_modules/my-packagemelalui babel juga dan kemudian mencoba:

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

Tetapi bahkan ini mengimpor setiap kelas yang diekspor oleh index.js. Satu-satunya hal yang tampaknya bekerja seperti yang saya inginkan adalah jika saya melakukannya:

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

Tapi saya lebih suka:

  1. Dapat mengimpor file yang diubah dan diperkecil sehingga saya tidak perlu memberi tahu webpack untuk menjalankan babel di dalam node_modules dan
  2. Mampu mengimpor setiap kelas individu langsung dari titik masuk saya daripada harus memasukkan path ke setiap file

Apa praktik terbaik di sini? Bagaimana orang lain mencapai pengaturan serupa? Saya perhatikan GlideJS memiliki versi ESM dari paketnya yang memungkinkan Anda untuk mengimpor hanya hal-hal yang Anda butuhkan tanpa harus menjalankan babel melaluinya misalnya.

Paket yang dimaksud: 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"
  }
}
Powerbuoy
sumber
1
Apakah Anda menambahkan mainatribut (entry point) di lib's package.json Anda? Periksa bangunan Anda. Dan bagaimana Anda menggabungkan paket lib Anda?
Abhishek
Properti utama package.json adalah arah ke titik masuk ke modul yang dijelaskan package.json. Dalam aplikasi Node.js, ketika modul dipanggil melalui pernyataan yang diperlukan, ekspor modul dari file yang disebutkan di properti utama akan menjadi apa yang dikembalikan ke aplikasi Node.js.
Abhishek
Ya, properti utama menunjuk ke index.js saya yang mengekspor semua kelas lainnya. Saya bundling file utama / index.js menggunakan webpack dan babel. Semuanya dijelaskan dalam pertanyaan.
powerbuoy
ini mungkin membantu Anda - danielberndt.net/blog/2018/...
Abhishek
Anda juga dapat mengimplementasikan implementasinya - github.com/mui-org/material-ui/blob/master/packages/material-ui/... Untuk mendapatkan ukuran build yang lebih pendek, lebih baik melakukannya import { MyClass } from 'my-package/src/MyClass';. Anda juga dapat menghapus kemasan src build untuk mempersingkat jalur file.
Abhishek

Jawaban:

1

Saya pikir ini adalah cara kerja webpack ketika Anda mencoba mengimpor bagian dari paket yang sudah dibundel?

Ya, cara Anda mengaturnya adalah mengimpor setiap kelas di index.js, yang kemudian diubah menjadi satu file (jika itu menargetkan ES5, yang paling umum *). Ini berarti bahwa ketika file itu diimpor dalam file lain, ia datang secara keseluruhan, dengan semua kelas tersebut.

Jika Anda ingin pengocokan pohon yang benar, Anda harus menghindari mentransformasikannya menjadi bundel CommonJS (ES5). Saran saya adalah menjaga modul ES6, baik dengan sendirinya atau di lokasi yang terpisah dari bundel ES5. Artikel ini akan membantu Anda memahami hal ini dan merekomendasikan instruksi. Pada dasarnya itu bermuara pada pengaturan lingkungan Babel menggunakan preset-env (sangat disarankan jika Anda belum menggunakannya!) Untuk mempertahankan sintaks ES6 . Berikut konfigurasi Babel yang relevan, jika Anda tidak ingin melakukan transpile ke ES5:

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

Artikel ini menjelaskan cara mengatur 2 bundel, masing-masing menggunakan sintaks modul yang berbeda.

Juga patut dicatat, dan agak disebutkan dalam artikel juga, Anda dapat mengatur titik entri modul ES di package.json. Itu memberitahu Webpack / Babel di mana modul ES6 dapat ditemukan, yang mungkin semua yang Anda butuhkan untuk kasus penggunaan Anda. Tampaknya kebijaksanaan konvensional mengatakan untuk melakukan:

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

Tetapi dokumentasi Node memilikinya sebagai:

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

Jika saya punya waktu saya akan bermain-main dengan ini dan melihat mana yang bekerja dengan benar, tetapi ini seharusnya cukup untuk membuat Anda berada di jalan yang benar.


* Bundel bertarget ES5 dalam format CommonJS, yang harus menyertakan semua file yang terkait, karena ES5 tidak memiliki dukungan modul asli. Itu datang dalam ES2015 / ES6.

CaitlinWeb
sumber
Saya mencoba menambahkan targets.esmodules: truedan sementara itu membuat perubahan pada skrip yang dibuat, itu tidak membuat perubahan apa yang diimpor pada akhirnya. Mengimpor satu kelas dari my-packagemasih mengimpor semuanya. Saya juga mencoba perubahan di package.json(bersama dengan perubahan lainnya) dan itu juga tidak mengubah apa pun. Nah, menambahkan type: modulebenar-benar merusak bangunan saya dengan "Harus menggunakan impor untuk memuat Modul ES: /sleek-ui/webpack.config.js memerlukan () modul ES tidak didukung." jadi saya harus menghapus sedikit itu. Saya akan melihat artikel yang ditautkan.
powerbuoy
Ok jadi artikel itu benar-benar menyuruh saya untuk mengatur modules: false(tidak di dalam targets) tapi itu tidak berhasil ... Saya pikir saya hanya akan mengimpor langsung dari file sumber dan terus menjalankan babel melalui node_modules sampai kita dapat menggunakan barang-barang ini secara asli.
powerbuoy
@ Powerbuoy Mengimpor dari file sumber berfungsi. Mungkin tidak jelas dari pos saya, dan jika itu masalahnya saya dapat mengeditnya, tetapi Anda hanya ingin mengimpor kelas yang disukai import MyClass from 'my-package/myClass';. Contoh repo yang baik dari ini adalah lodash-es .
CaitlinWeb
-1

Ini adalah use case yang valid. Tujuan utamanya adalah melakukan ini import { MyClass } from 'my-package'tetapi ada cara yang lebih bersih untuk melakukan ini.

Buat file indeks agregator di blog Anda my-package. Pada dasarnya my-package/index.jsdan akan terlihat seperti ini:

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

export { MyClass, MyOtherClass }

Maka Anda bisa melakukannya import { MyClass } from 'my-package'. Peasy mudah.

Selamat bersenang-senang!

idancali
sumber
Ini persis apa yang sudah saya lakukan, yang saya pikir cukup jelas dalam pertanyaan itu.
powerbuoy