He creado un paquete NPM relativamente pequeño que consta de aproximadamente 5 clases ES6 diferentes contenidas en un archivo cada una, todas se parecen a esto:
export default class MyClass {
// ...
}
Luego configuré un punto de entrada para mi paquete que se ve así:
export { default as MyClass } from './my-class.js';
export { default as MyOtherClass } from './my-other-class.js';
Luego ejecuté el punto de entrada a través de webpack y babel terminando con un index.js transpilado y minificado
Instalar e importar el paquete funciona bien, pero cuando hago lo siguiente desde mi código de cliente:
import { MyClass } from 'my-package';
No solo importa "MyClass", importa todo el archivo, incluidas todas las dependencias de cada clase (algunas de mis clases tienen enormes dependencias).
Supuse que así es como funciona webpack cuando intentas importar partes de un paquete ya incluido. Así que configuré mi configuración de paquete web local para ejecutar también a node_modules/my-package
través de babel y luego intenté:
import { MyClass } from 'my-package/src/index.js';
Pero incluso esto importa todas las clases exportadas por index.js. Lo único que parece funcionar de la manera que quiero es si lo hago:
import MyClass from 'my-package/src/my-class.js';
Pero prefiero mucho:
- Poder importar el archivo transpilado y minificado para que no tenga que decirle a webpack que ejecute babel dentro de node_modules y
- Poder importar cada clase individual directamente desde mi punto de entrada en lugar de tener que ingresar la ruta a cada archivo
¿Cuál es la mejor práctica aquí? ¿Cómo logran otros configuraciones similares? Me di cuenta de que GlideJS tiene una versión ESM de su paquete que le permite importar solo las cosas que necesita sin tener que ejecutar babel a través de él, por ejemplo.
El paquete en cuestión: 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"
}
}
fuente
main
atributo (punto de entrada) en el paquete package.json de su lib? Revisa tu construcción. ¿Y cómo está empaquetando su paquete lib?import { MyClass } from 'my-package/src/MyClass';
. También puede eliminar el paquete de compilación src para acortar la ruta del archivo.Respuestas:
Sí, la forma en que lo configuró es importando cada clase en index.js, que luego se transpira en un archivo (si está dirigido a ES5, que es lo más común *). Esto significa que cuando ese archivo se importa en otro archivo, viene en su totalidad, con todas esas clases.
Si desea una correcta sacudida del árbol, debe evitar transpilarlo en un paquete CommonJS (ES5). Mi sugerencia es mantener los módulos ES6, ya sea solos o en una ubicación separada del paquete ES5. Este artículo debería ayudarlo a comprender completamente esto y tiene instrucciones recomendadas. Básicamente, se reduce a configurar el entorno de Babel usando preestablecido-env (¡muy recomendable si aún no lo está usando!) Para preservar la sintaxis de ES6 . Aquí está la configuración de Babel relevante, si no desea transpilar a ES5:
El artículo detalla cómo configurar 2 paquetes, cada uno con una sintaxis de módulo diferente.
También vale la pena señalar, y también se menciona en el artículo, puede establecer el punto de entrada del módulo ES en package.json. Eso le dice a Webpack / Babel dónde se pueden encontrar los módulos ES6, que pueden ser todo lo que necesita para su caso de uso. Parece que la sabiduría convencional dice hacer:
Pero la documentación del nodo lo tiene como:
Si tuviera tiempo, jugaría con esto y vería cuál funciona correctamente, pero esto debería ser suficiente para ubicarlo en el camino correcto.
* Los paquetes dirigidos a ES5 están en formato CommonJS, que tiene que incluir todos los archivos asociados, porque ES5 no tiene soporte para módulos nativos. Eso vino en ES2015 / ES6.
fuente
targets.esmodules: true
y aunque eso hizo cambios en el script creado, no hizo ningún cambio en cuanto a lo que se importó al final. Importar una sola clase demy-package
todavía importa todo. También probé los cambios enpackage.json
(junto con el otro cambio) y eso tampoco cambió nada. Bueno, agregartype: module
realmente rompió mi compilación con "Debe usar la importación para cargar el módulo ES: /sleek-ui/webpack.config.js require () de los módulos ES no es compatible". así que tuve que quitar ese bit Echaré un vistazo al artículo vinculado.modules: false
(no dentrotargets
) pero tampoco funcionó ... Creo que solo importaré directamente desde el archivo fuente y seguiré ejecutando babel a través de node_modules hasta que podamos usar esto de forma nativa.import MyClass from 'my-package/myClass';
. Un buen ejemplo de repositorio de esto es lodash-es .Este es un caso de uso válido. El objetivo final es hacer esto,
import { MyClass } from 'my-package'
pero hay una forma más limpia de hacerlo.Cree un archivo de índice agregador en su
my-package
. Básicamentemy-package/index.js
y debería verse así:Entonces puedes hacer
import { MyClass } from 'my-package'
. Pan comido.¡Que te diviertas!
fuente