A medida que nuestro proyecto de NPM crece y evoluciona con el tiempo, las dependencias que utilizamos en él también lo hacen. Crecen, se actualizan y evolucionan, añadiendo nuevas funcionalidades, corrigiendo problemas, y quizás hasta puede que perdiendo funcionalidades por el camino.
Es por todos estos factores por lo que conviene y es importante aprender una serie de buenas prácticas para mantener nuestro proyecto en buen estado, y que mencionaremos a lo largo de este artículo.
Semantic version (semver)
La forma más habitual de versionar nuestros paquetes es usando versionado semántico, muy frecuentemente abreviado como semver. Este tipo de versionado se basa en unas pautas concretas en el número de versión de los paquetes o proyectos. Dado un número de versión de un proyecto, este número está formado por 3 partes, por ejemplo: a.b.c
:
En nuestro ejemplo la versión es 2.0.4
:
-
2
es el major version. Se trata del número de versión mayor y se avanza uno más si las novedades del paquete, librería o proyecto van a producir cambios importantes (breaking changes) que no son compatibles con versiones anteriores. Si avanzaramos, pasaríamos a3.0.0
. Si tu paquete no está siendo usado en producción, puedes empezar con la versión0.1.0
en lugar de la1.0.0
. -
0
es el minor version. Se trata del número de versión menor y se avanza uno más si las novedades del paquete, librería o proyecto introducen nuevas funcionalidades que son compatibles con versiones anteriores. Si avanzaramos, pasaríamos a2.1.0
. -
4
es el patch version. Se trata del número utilizado para parches que corrigen problemas, bugs o defectos en funcionalidades del código actual y son compatibles con versiones anteriores. Si avanzaramos, pasaríamos a2.0.5
.
Por lo tanto, sabiendo estas características, observando el número de versión actual 2.0.4
y suponiendo que partimos de una primera versión 1.0.0
, sabemos que desde el lanzamiento de la v2
ha tenido 5 parches para corregir errores o problemas.
Si quieres obtener más información sobre versionado semántico, puedes consultar la página oficial semver.org, donde tienen algunas recomendaciones.
Dependencias del proyecto
La lista de dependencias del proyecto se guardan en el package.json
, en un apartado dependencies
y en otro devDependencies
, dependiendo si son paquetes de producción o de desarrollo respectivamente. Si queremos ver rápidamente, y desde una terminal cuales son las dependencias de nuestro proyecto, tenemos varias formas:
Podemos utilizar el comand npm list
para mostrar todas las dependencias del proyecto. No te olvides de poner el flag --depth 0
, de lo contrario nos mostrará todas las dependencias (y las dependencias de las dependencias), lo que probablemente se convertiría en una amalgama de paquetes:
$ npm list --depth 0
[email protected] /home/manz/github/frontend-project
├── [email protected]
├── [email protected]
└── [email protected]
Si queremos ver la diferencia entre los paquetes de desarrollo de los paquetes de producción, podemos simplemente echar un vistazo al fichero package.json
con nuestro editor, o hacer un cat package.json
filtrando mediante la herramienta jq:
$ cat package.json | jq .devDependencies,.dependencies
{
"gh-pages": "^3.1.0"
}
{
"howler": "^2.2.0",
"react": "^16.13.1"
}
De esta forma tenemos dos grupos bien diferenciados y sabemos que paquetes están en la versión de desarrollo y cuales en la de producción.
Control de dependencias
En la última forma mencionada, habrás observado que el número de versión de cada paquete, en algunas ocasiones viene precedido por un carácter especial, como ^
(circunflejo) o ~
(tilde). La ausencia o presencia de estos carácteres, junto a otros detalles, indica a npm
como debe controlar el proceso de actualizaciones.
Para entenderlo mejor, imagina el siguiente supuesto (no es real al 100%, luego explicaré por qué):
- Hoy, creamos un proyecto en el que instalaremos dos dependencias:
A
yB
. - Al instalarlas con
npm install <paquete>
se instala la última versión disponible. - En el caso de
A
, se instala la versión1.0.3
. En el caso deB
, la1.4.0
. - Todo funciona correctamente. Guardamos en
GitHub
y borramos el repo del equipo.
Retomamos el proyecto 3 meses después. Clonamos y hacemos un npm install
.
- Se instala la última versión disponible de cada dependencia.
- En el caso de
A
, la última versión es1.0.15
. En el caso deB
, la2.2.1
. - La dependencia
A
sigue funcionando correctamente, ya que sólo han habido parches. - La dependencia
B
falla, ya que el proyecto fue diseñado para la1.4.0
, y la versión2.2.1
introduce cambios grandes que no son compatibles.
Sin haber cambiado ni una línea de código, nuestro proyecto está fallando, y esto ocurre porque los cambios externos a nuestro código también influyen en nuestro proyecto. Cada vez que añadimos una dependencia a nuestro proyecto, como su propio nombre indica, dependemos también de esa librería, código o sistema.
IMPORTANTE: Para evitar situaciones como estas, npm incluye un
^
(circunflejo) antes del número de versión de la dependencia. De esta forma, sabemos que versión se instaló por primera vez, pero npm buscará la versión más alta disponible, teniendo en cuenta sólo parches y actualizaciones menores, pero nunca instalando actualizaciones mayores.
Política de actualizaciones
Si echamos un vistazo a las versiones de las dependencias de nuestro proyecto, veremos que tienen uno de los siguientes formatos:
Versión | Descripción |
---|---|
1.2.4 | Instala siempre la versión indicada 1.2.4 . |
^1.2.4 | Instalará la versión menor más alta, sin llegar a pasar a la versión 2.x ni superiores. Por defecto. |
~1.2.4 | Instalará sólo los parches, sin llegar a pasar a la versión 1.3 ni superiores. |
También es posible utilizar el carácter x
o *
para indicar comodines en la versión. Por ejemplo, la versión 1.2.x
sería equivalente a ^1.2.0
, la versión 1.x
sería equivalente a ^1.0
y la versión *
o x
sería equivalente a cualquier versión. Puedes utilizar la «calculadora» de semver.npmjs.com para hacer pruebas y comprender el funcionamiento del versionado semántico.
Podemos controlar la versión que se instalará y la política de actualizaciones dependiendo de como instalemos los paquetes:
# Busca e instala la última versión disponible (usará ^2.3.1)
# (sólo permitirá actualización de versiones menores y parches)
$ npm install lit-element
# Instala la versión exacta 2.3.1
# (no permitirá actualizaciones a otras versiones)
$ npm install [email protected]
Recuerda que siempre puedes editar manualmente el package.json
y cambiar el caracter de versión si así lo deseas.
Actualización de dependencias
NPM tiene un sistema de actualización de dependencias integrado. Puedes activarlo ejecutando npm outdated
, lo que mostraría algo parecido a lo siguiente:
$ npm outdated
Package Current Wanted Latest Location
gh-pages 0.4.0 0.4.0 3.1.0 frontend-project
lit-element 2.0.1 2.3.1 2.3.1 frontend-project
Este menú nos mostrará las dependencias que tienen actualizaciones. En la columna current la versión instalada en el proyecto, en la columna wanted la versión máxima determinada por nuestro versionado semántico del package.json
y en latest la última versión disponible de la dependencia. Además, los colores (aunque bajo mi opinión, no muy adecuados) son importantes:
- En rojo se nos mostrarán los paquetes que encajan con nuestra política de actualizaciones semver y podemos actualizar.
- En amarillo se nos mostrarán los paquetes que no encajan con nuestra política de actualizaciones.
Sin embargo, existe un paquete no oficial llamado npm-check que mejora considerablemente el sistema de actualización de dependencias de NPM, con un sistema interactivo, mucha información bien desglosada y algunos parámetros interesantes:
Parámetro | Descripción |
---|---|
-u | Abre el menú interactivo de actualización. SPACE seleccionas paquetes y ENTER actualiza selección. |
-y | No abre menú interactivo, sino que actualiza todo sin preguntar. |
-p | No actualiza las dependencias de desarrollo, sólo las de producción. |
-d | No actualiza las dependencias de producción, sólo las de desarrollo. |
-g | Actualiza los paquetes globales del sistema, no los del proyecto. |
Así pues, para actualizar las dependencias de nuestro proyecto, tras instalarlo de forma global con npm install -g npm-check
, podemos ejecutarlo en modo interactivo con un simple npm-check -u
:
Observa que npm-check separa las actualizaciones en grupos de parches, versiones menores y versiones mayores, mostrando el número de versión actual y a actualizar, el texto devDep
si es una dependencia de desarrollo, e incluso un enlace clickeable al README o la página de la dependencia para revisar los cambios.