Come esportare più moduli ES6 da un pacchetto NPM


16

Ho creato un pacchetto NPM relativamente piccolo composto da circa 5 classi ES6 diverse contenute in un file ciascuna, che assomigliano tutte in questo modo:

export default class MyClass {
    // ...
}

Ho quindi impostato un punto di ingresso per il mio pacchetto simile al seguente:

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

Ho quindi eseguito il punto di ingresso attraverso webpack e babel finendo con un index.js traspilato e minimizzato

L'installazione e l'importazione del pacchetto funziona correttamente, ma quando eseguo le seguenti operazioni dal mio codice client:

import { MyClass } from 'my-package';

Non importa solo "MyClass", importa l'intero file comprese tutte le dipendenze di ogni classe (alcune delle mie classi hanno dipendenze enormi).

Ho pensato che funziona così il webpack quando provi a importare parti di un pacchetto già in bundle? Quindi ho impostato la mia configurazione webpack locale per eseguire node_modules/my-packageanche babel e poi ho provato:

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

Ma anche questo importa ogni singola classe esportata da index.js. L'unica cosa che sembra funzionare nel modo che voglio è se lo faccio:

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

Ma preferirei molto:

  1. Essere in grado di importare il file traspilato e minimizzato in modo da non dover dire a webpack di eseguire babel all'interno di node_modules e
  2. Essere in grado di importare ogni singola classe direttamente dal mio punto di ingresso invece di dover inserire il percorso di ciascun file

Qual è la migliore pratica qui? In che modo altri ottengono configurazioni simili? Ho notato che GlideJS ha una versione ESM del suo pacchetto che ti consente di importare solo le cose di cui hai bisogno senza doverlo eseguire, ad esempio.

Il pacchetto in questione: 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
Hai aggiunto l' mainattributo (entry point) in package.json della tua lib? Controlla la tua build. E come stai raggruppando il tuo pacchetto lib?
Abhishek

La proprietà principale di un package.json è una direzione verso il punto di ingresso al modulo che sta descrivendo package.json. In un'applicazione Node.js, quando il modulo viene chiamato tramite un'istruzione request, le esportazioni del modulo dal file indicato nella proprietà principale saranno quelle restituite all'applicazione Node.js.
Abhishek,

Sì, la proprietà principale punta al mio index.js che esporta tutte le altre classi. Sto raggruppando il file principale / index.js usando webpack e babel. È tutto spiegato nella domanda.
powerbuoy

questo potrebbe aiutarti - danielberndt.net/blog/2018/…
Abhishek

Puoi anche implementare la loro build - github.com/mui-org/material-ui/blob/master/packages/material-ui/… Per avere una build più corta è meglio farlo import { MyClass } from 'my-package/src/MyClass';. Puoi anche rimuovere il packaging di src build per abbreviare il percorso del file.
Abhishek

Risposte:


1

Ho pensato che funziona così il webpack quando provi a importare parti di un pacchetto già in bundle?

Sì, il modo in cui l'hai configurato è l'importazione di ogni classe in index.js, che viene quindi trasferita in un unico file (se è destinato ES5, che è più comune *). Ciò significa che quando quel file viene importato in un altro file, viene nella sua interezza, con tutte quelle classi.

Se si desidera una corretta agitazione dell'albero, è necessario evitare di trasferirlo in un pacchetto CommonJS (ES5). Il mio suggerimento è di conservare i moduli ES6, da soli o in una posizione separata dal bundle ES5. Questo articolo dovrebbe aiutarti a comprenderlo appieno e contiene istruzioni consigliate. Fondamentalmente si riduce all'impostazione dell'ambiente Babel usando preset-env (altamente raccomandato se non lo si sta già utilizzando!) Per preservare la sintassi ES6 . Ecco la configurazione Babel pertinente, se non si desidera eseguire la transpile su ES5:

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

L'articolo spiega come impostare 2 bundle, ognuno utilizzando una sintassi del modulo diversa.

Vale anche la pena notare, ed è un po 'menzionato nell'articolo, è possibile impostare il punto di ingresso del modulo ES in package.json. Questo dice a Webpack / Babel dove si trovano i moduli ES6, che potrebbe essere tutto ciò che serve per il tuo caso d'uso. Sembra che la saggezza convenzionale dice di fare:

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

Ma la documentazione di Node lo ha come:

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

Se avessi tempo, ci proverei e vedrei quale funziona correttamente, ma questo dovrebbe bastare a metterti sulla strada giusta.


* I bundle con targeting ES5 sono in formato CommonJS, che deve includere tutti i file associati, poiché ES5 non ha il supporto del modulo nativo. È arrivato in ES2015 / ES6.


Ho provato ad aggiungere targets.esmodules: truee mentre ciò ha apportato modifiche allo script incorporato, non ha apportato modifiche a ciò che è stato importato alla fine. L'importazione di una singola classe da my-packageimporta ancora tutto. Ho anche provato le modifiche in package.json(insieme all'altra modifica) e anche quello non ha cambiato nulla. Bene, l'aggiunta in type: modulerealtà ha rotto la mia build con "Deve usare l'importazione per caricare il modulo ES: /sleek-ui/webpack.config.js che richiedono () dei moduli ES non è supportato." quindi ho dovuto rimuovere quel pezzetto. Dò un'occhiata all'articolo collegato.
powerbuoy

Ok, quindi l'articolo in realtà mi ha detto di impostare modules: false(non all'interno targets) ma neanche quello ha funzionato ... Penso che mi limiterò a importare direttamente dal file sorgente e continuerò a far funzionare babel attraverso node_modules fino a quando non possiamo usare queste cose in modo nativo.
powerbuoy

@powerbuoy L'importazione dal file sorgente funziona. Forse non era chiaro dal mio post, e in tal caso posso modificarlo, ma vuoi importare solo la classe come import MyClass from 'my-package/myClass';. Un buon esempio di repository di questo è lodash-es .
CaitlinWeb

-1

Questo è un caso d'uso valido. L'obiettivo finale è quello di farlo, import { MyClass } from 'my-package'ma c'è un modo più pulito di farlo.

Crea un file indice aggregatore nel tuo my-package. Fondamentalmente my-package/index.jse dovrebbe apparire così:

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

export { MyClass, MyOtherClass }

Quindi puoi farlo import { MyClass } from 'my-package'. Vai tranquillo.

Divertiti!


Questo è esattamente quello che sto già facendo, che pensavo fosse abbastanza chiaro nella domanda.
powerbuoy
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.