Crear un componente en Svelte y publicarlo en NPM
Tags: svelte, npm, package, node
Published: 2021-02-15 17:08:01

## 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](https://svelte.dev/), podrías crearte un componente, publicarlo en un package de [NPM](https://www.npmjs.com/) 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](https://alextomas.com/blog/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](https://www.npmjs.com/).
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`:

```bash
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í:

```bash
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:

```bash
$ 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.

```bash
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):

```json
{
  "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:

```javascript
// 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í:

```html
<!-- 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`:

```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í:

```json
{
  "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:

```javascript
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:

```bash
npx degit sveltejs/template svelte-playground
```

Luego accedemos al proyecto e instalamos las dependencias:

```bash
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:

```bash
npm link
```

Y recibirás una respuesta similar a:

```bash
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:

```bash
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](https://alextomas.com/blog/crear-componente-svelte-publicar-npm-package/alex_tomas_com_2021-02-15-17-07-14.png)

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

```html
<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](https://alextomas.com/blog/crear-componente-svelte-publicar-npm-package/alex_tomas_com_2021-02-15-17-10-49.png)

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:

```html
<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](https://alextomas.com/blog/crear-componente-svelte-publicar-npm-package/alex_tomas_com_2021-02-15-17-19-03.png)

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

```html
<AtButton title="Enviar formulario" />
```

Quedando:

![Botón con estilos y la propiedad title](https://alextomas.com/blog/crear-componente-svelte-publicar-npm-package/alex_tomas_com_2021-02-15-17-20-25.png)

## 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...

```bash
npm publish --access public
```

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

![Publicación de paquete en NPM con éxito](https://alextomas.com/blog/crear-componente-svelte-publicar-npm-package/alex_tomas_com_2021-02-15-17-27-33.png)

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](https://alextomas.com/blog/crear-componente-svelte-publicar-npm-package/alex_tomas_com_2021-02-15-17-29-25.png)


## ¿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:

```bash
npm i @alextomas80/svelte-custom-button
```

Y el uso es igual que el de antes:

```html
<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](https://alextomas.com/blog/crear-componente-svelte-publicar-npm-package/alex_tomas_com_2021-02-15-17-33-00.png)

> 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

[![¿Me invitas a un café?](https://alextomas.com/img/ko-fi.png)](https://ko-fi.com/alextomas#checkoutModal)

Y esto es todo. Espero que te pueda servir 😉