Creando componentes en React y publicando en NPM

Ivan B. Trujillo
CanariasJS
Published in
9 min readJul 5, 2018

--

Photo by Iker Urteaga on Unsplash

En esta entrada hablaré sobre como crear componentes en react y publicarlos en NPM para poder reutilizarlos en cualquier otro proyecto o compartirlos con la comunidad.

Debemos de tener en cuenta que cuando desarrollamos un componente dentro de nuestro proyecto y lo utilizamos, el webpack del proyecto ya se encarga de transpilarlo y si dicho componente tiene dependencias, las instalamos a nivel de proyecto y ya las podemos usar. Cuando haces un componente reutilizable, debes de hacer eso mismo pero a nivel de tu componente, es decir, tu componente tendrá su propia configuración de webpack para transpilarlo y su propio package.json donde se definan sus versiones, dependencias, licencia, etc.

Estructura

Cada componente tendrá la siguiente estructura:

estructura del componente
  • build: en esta carpeta es donde se generará nuestro componente minificado y transpilado a ES5, el cual usarán las aplicaciones cuando lo instalen via NPM.
  • config: aquí definimos la configuración de webpack para transpilar y minificar nuestro componente. Podemos adaptarla y modificarla en función del componente que estemos desarrollando
  • node_modules: aquí es donde se instalaran las dependencias de nuestro componente. Esta carpeta es utilizada solo cuando lo estamos desarrollando, ya que luego no serán subidos ni a NPM ni a nuestro repositorio en git. Como es habitual, no hay que crearla, se generará automáticamente cuando hagamos un npm install.
  • src: aquí incluiremos el contenido de nuestro componente. Su estructura puede variar en base a nuestras necesidades. El código que definamos aquí es el que webpack transpilará y minificará para dejar el resultado en la carpeta build.
  • .babelrc: archivo de configuración de babel. Aquí definimos como queremos que babel transpile nuestro JS.
  • .eslintrc: es recomendado tener un linter en el código que detecte los errores de formato de código. En este archivo configuramos sus reglas.
  • .eslintignore: indicaremos que queremos que el eslint no lintee, como por ejemplo los ficheros css.
  • .gitignore: es donde definimos los archivos y carpetas que no deben subirse a git.
  • .npmignore: igual que el .gitignore, solo que ignorará los archivos que no queramos subir a npm.
  • package.json: fichero donde definimos el nombre, version, licencia, scripts, dependencias, etc de nuestro componente.

Inicializando el proyecto

Lo primero que debemos de hacer es crear la carpeta de nuestro proyecto de componente e inicializarlo:

mkdir mi-componente && cd mi-componente && npm init

Cuando nos pregunte por el entry point, debemos de indicar: build/index.js

Lo siguiente que haremos será crear las carpetas build, config y src.

mkdir build config src

Creamos el resto de ficheros del proyecto los cuales rellenaremos luego:

touch .babelrc .eslintrc .gitignore .npmignore index.js README.md config/webpack.config.js

Dependencias

Vamos a instalar las dependencias de nuestro componente. Como publicaremos nuestro componente en npm, todas las dependencias irán en devDepedencies.

Babel: es el encargado de transpilar nuestro código javascript y jsx a ES5. Para ello necesitamos instalar babel-cli, babel-core, babel-loader, babel-preset-es2015 y babel-preset-react. Además, instaleremos babel-eslint para lintear el codigo.

npm install --save-dev babel-cli babel-core babel-loader babel-preset-es2015 babel-preset-react babel-eslint

EsLint: es el encargado de detectar que nuestro código esté bien escrito, siguiendo las reglas que le configuremos. En este caso usaremos las reglas de airbnb, + jsx-a11y (requerido por airbnb), junto con el plugin para lintear los imports de +ES6, el plugin para lintear React y JSX y eslint-watch para que este revisando nuestro código en cada cambio.

npm install --save-dev eslint eslint-config-airbnb eslint-plugin-jsx-a11y eslint-plugin-react eslint-plugin-import eslint-watch 

Webpack: para poder hacer el build de nuestro componente necesitamos webpack, así como los plugins de style-loader y css-loader para que interprete nuestro css.

npm install --save-dev webpack webpack-cli style-loader css-loader

React y PropTypes: Por ultimo instalaremos react y prop-types, que son las librerías que utilizará nuestro componente.

npm install --save-dev react proptypes

A continuación vamos a configurar cada uno de los ficheros y finalmente definiremos los scripts en nuestro package.json para operar con nuestro componente.

Configurando el entorno

.babelrc

Le añadimos los presets de es2015 y react para soportar React y ES6

{
"presets": ["es2015", "react"]
}

.eslintrc

Añadiremos como parser a babel-eslint, extenderemos de la configuración de airbnb y añadiremos los plugins de import y react. Además añadiremos algunas reglas personalizadas:

{
"parser": "babel-eslint",
"extends": ["eslint:recommended", "airbnb"],
"plugins": ["import", "react"],
"env": {
"es6": true,
"jest": true
},
"rules": {
"semi": ["error", "never"],
"comma-dangle": ["error", "always-multiline"],
"import/newline-after-import": [
"error",{ "count": 2 }
],
"max-len": "off",
"import/no-absolute-path": 0,
"import/extensions": 0,
"no-mixed-operators": 0,
"import/no-unresolved": "off",
"react/jsx-filename-extension": [
"error",{ "extensions": [".js", ".jsx"] }
],
"no-restricted-properties": [
"error",{ "property": "lenght" }
],
"import/no-extraneous-dependencies": [
"error",{ "packageDir": "./" }
]
}
}

Ignorando cosas!

.eslintignore: Indicaremos que ignore los ficheros css y la carpeta build:

*.css
build

.gitignore: Indicaremos que ignore la carpeta build, node_modules, los ficheros de log y los ficheros .DS_Store que generan los sistemas operativos de Mac.

.DS_Store
build
node_modules
*.log

.npmignore: Indicaremos que ignore lo que no queremos subir a npm, que son por ejemplo el core de nuestro modulo ya que solo lo subiremos ya transpilados, así como los ficheros de configuración de babel, eslint, etc. Esto es todo opcional y depende de cada persona. Yo prefiero subir a npm solo lo que voy a utilizar y en git tener el código completo y las configuración de desarrollo.

.babelrc
.eslintignore
.eslintrc
.gitignore
config
src

Con esta configuración solo subiremos a npm el build, junto con el README.md y el package.json. No hace falta ignorar node_modules ya que npm lo hace por si solo.

Configurando webpack

Antes de empezar a desarrollar el componente, debemos de configurar webpack para que lo pueda transpilar.

En esta configuración lo que hacemos es transpilar el contenido de src y volcarlo en la carpeta /build. Además, añadimos el babel-loader para que interprete nuestro código correctamente y los style-loader y css-loader para hacer lo propio con el css.

Por ultimo, en las externals le indicamos que dependencias no debe compilar, ya que el modulo en producción las utilizará del proyecto en el que se encuentre. Estas son React y PropTypes.

Aunque no las compile, debe resolverlas mientras estemos desarrollando el modulo. Para ello lo definimos en resolve.

const path = require('path')module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, '../build'),
filename: 'index.js',
libraryTarget: 'commonjs2',
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|build)/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.css$/,
exclude: /(node_modules|build)/,
use: ['style-loader', 'css-loader'],
},
],
},
externals: {
react: {
commonjs: 'react',
commonjs2: 'react',
amd: 'React',
root: 'React',
},
'prop-types': {
commonjs: 'prop-types',
commonjs2: 'prop-types',
amd: 'PropTypes',
root: 'PropTypes',
},
},
resolve: {
extensions: ['.js', '.jsx', '.css'],
alias: {
react: path.resolve(__dirname, './node_modules/react'),
'prop-types': path.resolve(
__dirname,
'./node_modules/prop-types'
),
'styled-components': path.resolve(
__dirname,
'./node_modules/styled-components'
),
},
},
}

Configurando el package.json

Nuestro package.json debe parecerse a esto:

Vamos a añadirle los scripts para lanzar webpack en modo desarrollo y transpilar el modulo para producción:

"scripts": {
"start": "webpack --mode development --config config/webpack.config.js --watch",
"build": "webpack --mode production --config config/webpack.config.js --progress"
}

Creando y probando nuestro componente

Simplemente vamos a crear en src/index.js un componente dumb que muestre un texto.

import React from 'react'export default () => (
<h1>
Este es mi componente
</h1>
)

Y ejecutamos nuestro script start, que compilará nuestro componente y se quedará en modo watch para detectar cambios.

Ahora para probar nuestro componente, vamos a crear un proyecto simple en react con create-react-app en otra carpeta. Si no tienes instalado create-react-app, instálalo con este comando:

npm install -g create-react-app

Y generamos el proyecto para probar:

create-react-app component-tester

Abrimos el proyecto e importamos nuestro componente como si estuviera en NPM en src/App.js:

.....
import MiComponente from 'mi-componente'
.....
<MiComponente />
.....

Arrancamos el proyecto:

npm start

Y obviamente va a devolvernos un error porque no encuentra el modulo. Para probar el modulo localmente, debemos linkearlo en nuestro sistema.

Para ello debemos de seguir dos pasos:

1. Linkear el componente a publicar:
Desde la carpeta de nuestro componente (mi-componente), ejecutamos:

npm link

El componente se linkeara en nuestro sistema como mi-componente, y podemos utilizarlo en el resto de proyectos.

2. Referenciarlo en el proyecto de component-tester:
Desde la carpeta de este proyecto, referenciamos el componente mi-componente que acabamos de linkar en el paso anterior. Con esto conseguimos que en el proyecto, en vez de buscar el componente en node_modules, lo busque en nuestro sistema.:

npm link mi-componente

Si ahora volvemos a arracar el proyecto component-tester, funcionará sin problemas:

Si hacemos cualquier cambio en mi-componente, al tener webpack en modo watch podemos ver los cambios instantáneamente en el proyecto component-tester.

Publicando en NPM

Antes de publicar, necesitamos modificar el package.json para añadir dos cosas: por un lado debe de de hacerse se haga un build siempre antes de publicar y por otro lado, nuestro componente no debe instalar react como dependencia cuando sea instalado en otro proyecto, ya que deberá usar el react que tenga instalado el proyecto en cuestion.

Para el primer caso, creamos el script prepublishOnly en nuestros scripts:

"prepublishOnly": "npm run build",

Y añadimos react en las peerDependencies del package.json para que no se instale cuando se instale desde npm:

"peerDependencies": {
"react": "^15.5.4"
}

Nuestro package.json quedaría asi:

Por último, para publicar en npm necesitamos crearnos una cuenta y logearnos por consola:

npm login

Y finalmente, publicamos nuestro componente:

npm publish

Se ejecutará el script de prepublishOnly y se compilará nuestro componente. Al finalizar, se subirá a npm.

Ahora vamos a probar que nuestro componente funciona correctamente desde npm. Para ello en el proyecto component-tester, deslinkamos el componente local:

npm unlink mi-componente

Arrancamos el server para comprobar que da error de nuevo, y a continuación instalamos nuestro componente desde npm:

npm install mi-componente

Aqui puedes ver como se ha instalado correctamente.

Y si arracamos de nuevo nuestro proyecto, funcionará como esperabamos.

Con esta guía ya puedes publicar tus componentes React en NPM y reutilizarlos tanto en tus proyectos como en tu empresa.

Por último, seguramente estarás subiendo componentes para jugar que luego no quieres que estén en tu cuenta de npm. Para despublicar un paquete ejecuta este comando desde tu carpeta del componente:

npm unpublish -f

Conclusiones y recomendaciones finales

Aparte de lo explicado aquí, recomiendo varias cosas que no he cubierto en esta entrada, ya que se desviaban del objetivo:

  • Debes añadir un repositorio de git para gestionar el control de versiones y referenciarlo en el package.json, para que quienes tengan problemas con tu componente o quieran hacer propuestas puedan hacerlo.
  • Documenta tu componente. Tu README.md debe de estar rellenado de forma que explique correctamente como usar tu componente.
  • Recomiendo usar styled components para crear componentes reutilizables.
  • Si quieres publicar varios componentes, create dentro de src/ una carpeta de componentes y expórtalos en un index. De esta forma te puedes crear una librería de componentes fácilmente.
  • Para probar nuestros componentes, recomiendo también que el proyecto component-tester que aquí hicimos con un create-react-app, lo modifiques para utilizar storybook. Aparte de para testear los componentes, te servirá para exponerlos visualmente y crearte una librería.

Espero que te haya gustado el post!

--

--