Crear un componente en Svelte y publicarlo en NPM

Introducción

A lo largo de los años me he encontrado en proyectos que tienen diferentes áreas dentro de ese mismo proyecto (web, app, panel de administración…) y se tiene que mantener el aspecto de ciertos componentes en todas las partes de la aplicación.

Un ejemplo sencillo sería el típico botón con el color corporativo. Resulta que ese botón lo quieres utilizar en diferentes proyectos de ese mismo cliente, y el botón va a ser siempre IGUAL. ¿Qué hacemos?

Pues aquí tenemos tres opciones, a mi modo de ver:

  • Crear el botón las X veces que nos haga falta para los diferentes proyectos. Esto está "bien" pero… ¿y si se cambia, por ejemplo, el borde del botón (border-radius)? Lo tendrías que cambiar en todos los botones que te hayas creado en los diferentes proyectos. Ñeeee, bueno 💩
  • Si tu proyecto va a usar siempre la misma tecnología, por ejemplo Svelte, podrías crearte un componente, publicarlo en un package de NPM y re-utilizarlo allá donde te haga falta. Es lo que veremos en este artículo 🚀
  • Pero… ¿si ese mismo proyecto lo vas a usar con diferentes frameworks? por ejemplo, lo quieres usar en React, Svelte y otro en Vue. ¿Qué hacemos?. La respuesta es webcomponents. Te dejo aquí un artículo que escribí hace unos días para crear webcomponents con StencilJS.

Entonces, ahora vamos a ir a la segunda opción y haremos lo siguiente:

  • Nos crearemos un componente botón con Svelte.
  • Lo probaremos localmente.
  • Lo publicaremos en un package de NPM.
  • Lo probaremos de nuevo instalando la dependencia.

¿Qué vamos a necesitar?

  1. Tener cuenta en NPM.
  2. Tener Node y NPM instalados en tu equipo.
  3. Ganas de experimentar 🙃

¿Qué componente haremos?

Posiblemente tengas cientos de paquetes en NPM de botones. La idea no es reinventar la rueda, sino conocer el flujo de la creación de un componente y la publicación del paquete. Más adelante te animo que intentes crearte un componente que te solucione A TÍ algún problema concreto. Eso te ayudará a aprender y entender todos los conceptos.

Para nuestro ejemplo crearé un componente que se llamará <At-Button />.

Iniciamos el proyecto

Bien, lo primero que necesitaremos es una carpeta vacía para iniciar nuestro ¡pequeño gran proyecto!. Desde la consola, por ejemplo, creamos una carpeta y accedemos a la misma, en mi ejemplo la llamaré svelte-custom-button:

mkdir svelte-custom-button
cd svelte-custom-button

Ahora iniciamos nuestro package.json. No es necesario que lo escribas a mano, con el siguiente comando lo hará casi todo por tí:

npm init -y

Esto va a crear un archivo package.json y nos rellenará la información sobre el paquete, como el nombre, autor, descripción, las dependencias que utiliza, la versión etc…

Tendrás la siguiente respuesta:

$ npm init -y
Wrote to /RUTA_DE_TU_PROYECTO/svelte-custom-button/package.json:
{
  "name": "svelte-custom-button",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "alextomas80 <XXXXXXXXXXX@gmail.com>",
  "license": "MIT"

En mi caso author ya está completo porque estoy logeado en npm con mi usuario alextomas80.

Puedes rellenar o modificar los campos como el name, description, keywords, author y license conforme consideres.

Nota: En el name he añadido mi usuario de npm, en mi caso @alextomas80, añade el tuyo ahí.

El siguiente paso será la instalación de dependencias que usará nuestro paquete.

npm install -D rollup rollup-plugin-node-resolve rollup-plugin-svelte svelte

Nuestro package.json debe de estar de la siguiente forma (yo he cambiado la descripción y keywords):

{
  "name": "@alextomas80/svelte-custom-button",
  "version": "1.0.0",
  "description": "Ejemplo básico de creación de componente Svelte y paquete en NPM",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": ["svelte", "ejemplo"],
  "author": "alextomas80 <XXXXXXXXXXX@gmail.com>",
  "license": "MIT",
  "devDependencies": {
    "rollup": "^2.39.0",
    "rollup-plugin-node-resolve": "^5.2.0",
    "rollup-plugin-svelte": "^7.1.0",
    "svelte": "^3.32.3"
  }
}

Creación del componente

Vamos a necesitar una carpeta que se llamará src en la raíz del directorio, y dentro un archivo index.js. En ella contendremos el punto de inicio de nuestro proyecto y el componente de ejemplo.

Nuestro src/index.js quedará de la siguiente forma:

// src/index.js
import AtButton from "./AtButton.svelte";
export default AtButton;

Y como te habrás imaginado, ahora debemos de crear nuestro componente AtButton.svelte, quedando de momento algo así:

<!-- src/AtButton.svelte -->
<div>Botón de ejemplo</div>

Esto es un fichero de Svelte como seguramente conoces. Puedes importar librerías u otros elementos sin problemas. Así como poner estilos y demás (ahora luego lo veremos).

package.json

Tenemos que hacer unos pequeños cambios al package.json:

    // ...
    "main": "dist/index.js",
    "module": "dist/index.mjs",
    "svelte": "src/index.js",
    "scripts": {
        "build": "rollup -c",
        "dev": "rollup -c -w"
    },
    // ...
  • En main vamos a establecer el output después de correr el script build
  • En module vamos a definir lo mismo pero como salida un archivo .mjs, para que Node distinga entre módulos creados con CommonJS y ES6.
  • En svelte, vamos a definir la ruta de nuestro archivo index.js creado anteriormente.
  • Y por último definimos los scripts como para crear el build o correr en desarrollo.

Nota: la carpeta dist que hasbrás visto que apunta en main y module se generará automáticamente.

El package.json debería quedar así:

{
  "name": "@alextomas80/svelte-custom-button",
  "version": "1.0.0",
  "description": "Ejemplo básico de creación de componente Svelte y paquete en NPM",
  "main": "dist/index.js",
  "module": "dist/index.mjs",
  "svelte": "src/index.js",
  "scripts": {
    "build": "rollup -c",
    "dev": "rollup -c -w"
  },
  "keywords": ["svelte", "ejemplo"],
  "author": "alextomas80 <XXXXXXXXXXX@gmail.com>",
  "license": "MIT",
  "devDependencies": {
    "rollup": "^2.39.0",
    "rollup-plugin-node-resolve": "^5.2.0",
    "rollup-plugin-svelte": "^7.1.0",
    "svelte": "^3.32.3"
  }
}

Creamos el fichero rollup.config.js

Si has trabajado con Svelte en alguna otra ocasión, conocerás este archivo:

import svelte from "rollup-plugin-svelte";
import resolve from "rollup-plugin-node-resolve";

const pkg = require("./package.json");

export default {
  input: "src/AtButton.svelte",
  output: [
    { file: pkg.module, format: "es" },
    { file: pkg.main, format: "umd", name: "Name" },
  ],
  plugins: [svelte(), resolve()],
};

¿Cómo puedo probar mi componente localmente?

Antes de publicar el componente en el paquete de NPM, lo suyo sería probarlo para ver como queda y si estamos satisfechos con el resultado. Entonces, vamos a probarlo en un proyecto nuevo de Svelte. Para ello, nos crearemos un nuevo componente de Svelte, yo en mi caso me voy a crear un proyecto llamado svelte-playground ejecutando el siguiente comando:

npx degit sveltejs/template svelte-playground

Luego accedemos al proyecto e instalamos las dependencias:

cd svelte-playground
npm install

Bien. Ya tenemos lo siguiente:

  1. Un proyecto (svelte-custom-button) con nuestro componente, listo para ser probado.
  2. Un proyecto Svelte (svelte-playground) para probar el componente que enviaremos a NPM.

Primero, accedemos al proyecto del componente svelte-custom-button y ejecutamos:

npm link

Y recibirás una respuesta similar a:

npm WARN svelte-custom-button@1.0.0 No repository field.

audited 17 packages in 0.711s

3 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
/usr/local/lib/node_modules/svelte-custom-button -> /RUTA_DE_TU_PROYECTO/svelte-custom-button

Y luego, en el proyecto svelte-playground ejecutaremos npm link /la-ruta/de/tu-paquete, en mi caso:

npm link /RUTA_DE_TU_PROYECTO/svelte-custom-button

Con esto podremos probar localmente nuestro paquete. ¡Perfecto! ¡Y en tiempo real!

Usar componente localmente

Accede a tu proyecto svelte-playground y corre el proyecto con el comando npm run dev, con lo que verás la pantalla generada por Svelte en http://localhost:5000/:

Pantalla de inicio de Svelte

Y ahora editaremos nuestro fichero src/App.svelte dejándolo así:

<script>
  export let name;
  // Importamos nuestro componente Botón
  import AtButton from "@alextomas80/svelte-custom-button";
</script>

<main>
  <h1>Hello {name}!</h1>
  <p>Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn how to build Svelte apps.</p>

  <!-- Usamos el componente -->
  <AtButton />
</main>

<style>
  main {
    text-align: center;
    padding: 1em;
    max-width: 240px;
    margin: 0 auto;
  }

  h1 {
    color: #ff3e00;
    text-transform: uppercase;
    font-size: 4em;
    font-weight: 100;
  }

  @media (min-width: 640px) {
    main {
      max-width: none;
    }
  }
</style>

¡Y entonces tendremos nuestro botón! Quedando así:

Usando nuestro componente Svelte localmente

Bueno, no es un gran componente, pero por lo menos ya lo vemos.

Editar componente localmente a tiempo real

Vamos a darle estilo a nuestro botón. Volvemos al proyecto svelte-custom-button y dejaremos nuestro componente de la siguiente manera:

<script>
  // Le enviaremos al botón la propiedad `title`, en caso de no recibirla le ponemos valor por defecto
  export let title = "Botón";
</script>

<div class="boton">{title}</div>

<style>
  /* Aplicamos unos pocos estilos al botón */
  .boton {
    background-color: coral;
    border-radius: 3px;
    cursor: pointer;
    display: inline-block;
    padding: 10px 20px;
    transition: 0.3s ease;
  }
  .boton:hover {
    background-color: darkorange;
  }
</style>

Si accedes de nuevo a http://localhost:5000/ deberías ver los cambios reflejados automáticamente. El botón debería quedarte así:

Botón con estilos

Y si ahora le pasamos la propiedad title a nuestro <AtButton /> podemos personalizar el título del mismo:

<AtButton title="Enviar formulario" />

Quedando:

Botón con estilos y la propiedad title

Publicar el paquete en NPM

¡Perfecto! Ya tenemos nuestro componente, nos gusta y ahora lo queremos publicar en un paquete en NPM para usarlo en futuros proyectos. Bien, es muy fácil.

Deberás estar logeado en npm. Si no lo sabes si lo estás ejecuta: npm whoami (en mi caso me responde alextomas80, que es mi usuario de npm). Doy por hecho que tienes cuenta en NPM! 😂

Si no estás logeado: npm login

Es recomendable haber creado en el directorio raíz el archivo README.md con la información de nuestro paquete. Así otros usuarios podrán conocer la información de uso, propiedades, configuraciones etc etc…

npm publish --access public

Y si todo va bien deberías obtener lo siguiente:

Publicación de paquete en NPM con éxito

Ahora si tu perfil de npm (https://www.npmjs.com/settings/alextomas80/packages) verás que te aparecerá tu paquete. En el caso de que no lo haga, a veces tarda unos minutos en aparecer, puedes buscarlo directamente en el buscador, en mi caso buscaría @alextomas80/svelte-custom-button que será el nombre del paquete.

Paquete publicado en NPM

¿Cómo puedo probar mi componente desde NPM?

¡Muy fácil! Esto lo habrás hecho cientos de veces, bueno, igual alguna menos 😌 Accede a la información del paquete, en nuestro ejemplo: https://www.npmjs.com/package/@alextomas80/svelte-custom-button

Ahí nos indica como hacer la instalación a través de NPM:

npm i @alextomas80/svelte-custom-button

Y el uso es igual que el de antes:

<script>
  import AtButton from "@alextomas80/svelte-custom-button";
</script>

<main>
  <AtButton title="Acceder al panel" />
</main>

<style>
  main {
    text-align: center;
    padding: 1em;
    max-width: 240px;
    margin: 0 auto;
  }
</style>

Quedando de nuevo nuestro botón como lo teníamos:

Probando nuestro componente desde npm

Nota: Si no te sigue refrescando el componente desde el paquete local tendrás que hacer unlink. Accede al directorio del proyecto svelte-custom-button y ejecuta npm unlink y lo mismo en el proyecto Svelte svelte-playground ejecutando npm unlink /RUTA_DE_TU_PROYECTO/svelte-custom-button. Yo por lo menos tuve que hacerlo porque me seguía refrescando los cambios locales y no veía el componente de NPM 🙈

Repo de ejemplo: https://github.com/alextomas80/svelte-component-package-npm

Y esto es todo. Espero que te pueda servir 🙃