leanmind logo leanmind text logo

Blog

Código Sostenible

Cómo escribir código fácil de mantener mediante valores, principios y técnicas.

Cómo configurar un boilerplate con Typescript, Node.js, Jest y ESLint

Por Francisco Mesa

Con esta guía puedes crear tu propio empaquetado backend listo para funcionar con la configuración más habitual para Node.js y Typescript.

¿Qué es un boilerplate?

Normalmente se denomina boilerplate al código que es necesario utilizar en programación de forma repetitiva y con pocos o ningún cambio. Como puede leerse en la Wikipedia el origen viene de la prensa en papel, en la que se utilizaban esas planchas como rellenos.

Normalmente es el esqueleto de un proyecto en un lenguaje de programación que una vez instalado permite centrarse en implementar la solución y es muy habitual utilizarlo en las katas con el fin de centrarse en la práctica de codificar en lugar de crear el entorno de desarrollo.

¿Por qué crear un repositorio propio si ya existen otros?

Llevo unos desarrollando en el stack tecnológico Node.js - Typescript y quiero realizar diferentes katas de forma regular en este lenguaje por lo que tener un boilerplate me ahorrará tiempo cada vez que quiera empezar. Aunque hay muchos ejemplos en Github, entre ellos destaco algunos como el de Jakub Synowiec o el del canario Ulises Santana, necesitaba crear el mío propio. No por un afán inútil de reinventar la rueda, o encontrar que estos proyectos no tenían todo lo que necesitaban, sino como una tarea de aprendizaje y que la propia creación del proyecto se convierta en una especie de kata repetible en el futuro.

Características deseadas en el boilerplate

Por su propia definición, debería de ser tan sencillo como instalar y ya ponerse a programar. Por lo que además de poder programar en Typescript, utilizar características de Node.js y realizar tests en Jest (muy versátil para testear funcionalidad de código), quiero incorporar otras características como limitar la longitud del código con el linter. Además debe de contar con un README que permita a cualquier persona inicializar el proyecto y un test de ejemplo.

Al turrón…

Inicializo proyecto y repositorio remoto


mkdir typescript-boilerplate

cd typescript-boilerplate

npm init

Configuración inicial de package.json

El proyecto ya lo he creado en Github vacío. Con un README.md básico que solo tiene la descripción indicada para el proyecto en Node.js.


{

  "name": "typescript-boilerplate",

  "version": "1.0.0",

  "description": "Typescript and Node boilerplate template developer ready 🚀 to get started quickly with all basic tools included and configured.",

  "main": "index.js",

  "scripts": {

    "test": "echo \"Error: no test specified\" && exit 1"

  },

  "repository": {

    "type": "git",

    "url": "git+https://github.com/franciscomesa/typescript-boilerplate"

  },

  "keywords": [

    "typescript",

    "javascript",

    "node",

    "boilerplate",

    "jest",

    "nodejs",

    "eslint",

    "template",

    "starter-template",

    "nodejs-cli",

    "typescript-boilerplate",

    "node-typescript-boilerplate",

    "node-boilerplate",

    "backend"

  ],

  "author": "Francisco Mesa",

  "license": "MIT",

  "bugs": {

    "url": "https://github.com/franciscomesa/typescript-boilerplate/issues"

  },

  "homepage": "https://github.com/franciscomesa/typescript-boilerplate#readme"

}

Inicializar repositorio


git init

git remote add origin https://github.com/franciscomesa/typescript-boilerplate.git

git pull https://github.com/franciscomesa/typescript-boilerplate master

git push -u origin master

Creo y configuro el fichero .gitignore para que no tenga en cuenta paquetes, ficheros compilados y carpetas de los dos editores más populares

**.gitignore**

node_modules

build

.vscode

.idea

coverage


## Instalar paquetes Node.js

Instalo los paquetes necesarios. He incluido [Typedoc](https://typedoc.org/) para que pueda generarse documentación tras acabar la kata.

```bash

npm i typescript @types/node

npm i jest @types/jest ts-jest typedoc typedoc-plugin-markdown eslint --save-dev

Configuro Typescript (tsconfig.json)

Inicializo tsconfig.json el paquete que especifica las opciones de compilación con el comando tsc —init

Modifico algunos parámetros con la configuración del proyecto. Ficheros traspilados en la carpeta build e indico dónde estarán las carpetas los posibles ficheros de código typescript. Si solo fuese una carpeta utilizaría el objeto rootDir.


"outDir": "./build"

"rootDirs": ["./lib", "./test"]

Además, añado “exclude”: [“build”, “node_modules”, “test”] para que no tenga las carpetas de compilación, paquetes y tests ya que no necesitará la transpilación.

Inicializo y configuro el linter

Como no lo había instalado de forma global, anteriormente ejecuté npm i -g eslint y sigo los pasos guiados del script de ESLint.


eslint --init

Lo configuro para:

Al finalizar me recomienda instalar unos paquetes (sí, por favor)


@typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest

Configuración manual del linter

Al configurarlo me he dado cuenta de que hay algunos detalles que tengo que configurar a mano. Quizás sea por el orden en el que he ido configurando el proyecto.

Ignora código traspilado y paquetes incorporados:


"ignorePatterns": [

    "build",

    "node_modules"

  ],

Añado Jest como entorno para que reconozca las variables globales:


"env": {

    "es2021": true,

    "node": true,

    "jest": true

  },

Finalmente añado adicionalmente algunas reglas de ESLint. Alguna como la limitación de 80 caracteres, heredada de las tarjetas perforadas. Parece que lo adecuado para entornos de desarrollo desktop es tener 120 caracteres a la vista.


/* 

  Pedimos que nos alerte de variables que no estamos utilizando.

  Al hacerlo en Typescript, hay que desabilitar la regla por defecto del linter

*/

"no-unused-vars": "off",

"@typescript-eslint/no-unused-vars": ["warn"],

/* 

   Quiero que me avise de línea de código largas, pero no cuando se utilicen 

   expresiones regulares o literales que pueden ser URLS

*/

"max-len": ["warn", {

            "code":  120,

            "ignoreUrls": true,

            "ignoreComments": true, 

            "ignoreRegExpLiterals": true

            }

        ]

Configuración del editor

El fichero .editorconfig está soportado de forma nativa por muchos editores de código e IDEs sin necesidad de tener un plugin instalado y permite definir una serie de parámetros que hacen consistente editar el proyecto entre diferentes editores, IDEs, o equipos.

Evita muchos problemas al identar código, la diferencia del salto de línea entre Windows y otros sistemas operativos y la codificación de caracteres entre otros atributos.


root = true

[*]

charset = utf-8

end_of_line = lf

indent_size = 2

indent_style = space

insert_final_newline = true

trim_trailing_whitespace = true

[*.md]

max_line_length = 0

trim_trailing_whitespace = true

Sin tener aún un fichero de ejemplo ni test, el directorio del boilerplate empieza a coger forma con todo lo que he ido configurando. Aunque no es necesario tener configurado el linter, editor, repositorio remoto… si que ayuda a tener un buen punto de partida tras descargar e inicializar el proyecto en el que poder guardar la kata, continuarla en otro momento, compartirla, con las mejores condiciones posibles…

Estructura inicial de proyecto

Durante este proceso he ido actualizado el fichero README.md incorporando la información sobre los paquetes que he ido configurando y enlaces con los que ampliar información.

Configurar Jest

Antes de crear los directorios de código, script cliente, inicio la configuración de Jest. Creamos un fichero de configuración básico con jest —init.

El entorno de ejecución es Node.js que me configure el script por defecto de test en package.json.

Opciones para configurar un proyecto con Jest

El fichero jest.config.js por defecto necesita algunas modificaciones. En concreto:


roots: ["<rootDir>/test"],

collectCoverage: false,

coveragePathIgnorePatterns: [

    "/node_modules/",

    "build"

  ],

setupFilesAfterEnv: [

    "<rootDir>/test/setupTests.ts"

  ],

transform: {

    "^.+\\.(ts|tsx)$": "ts-jest"

  },

Si en este momento lanzamos los tests con npm run test se genera un error ya que no encuentra el fichero de setup indicado. No puedo posponer más crear esta carpeta y el fichero setupTests.ts vacío junto a un fichero de ejemplo de los tests first.test.ts:


describe('Initial test suit', () => {

  it('Simple add', () => {

    const expectedValue = 2

    expect(1+1).toBe(expectedValue)

  })

})

Estructura de proyecto para tests

Incorporo a package.json los scripts de tests:


"scripts": {

    "test": "jest",

    "test:coverage": "npm test -- --coverage",

    "test:watch": "npm test -- --watchAll",

  },

Tuneando un poco más Jest

Tras leer la entrada en Grow Configuración de Jest para proyectos en Typescript he incorporado algunas características adicionales en jest.config.js


resetMocks: true,

Acabando de configurar package.json

Linter

Incorporo los scripts del linter


"scripts": {

...

"lint": "eslint . --ext .ts",

"lint:fix": "npm run lint -- --fix",

}

Arranque del proyecto

Actualizo el arranque del proyecto y el directorio a incluir cuando se instala como dependencia. Quizás de este boilerplate surjan otros paquetes.


/* package.json */

"main": "build/index.js",

"files": [

    "build"

  ],

Creo el directorio con su fichero correspondiente


/* lib/index.ts */

console.log('Hello boilerplate\'s Typescript world!')

Incorporo los scripts del ciclo de vida predefinido en Node.js:


"prepare": "npm run build",

"prestart": "npm run build",

"start": "node build/index.js",

"build": "npm run lint && npm test && tsc",

Último detalle

Actualizo author en package.json:

"author": "Francisco Mesa [franciscomesa@gmail.com](mailto:franciscomesa@gmail.com) ([https://franciscomesa.es](https://franciscomesa.es/))",

One more thing…

¡La documentación! Aún no hemos configurado la documentación automática que habíamos incorporado al instalar paquetes Node.js. La forma más cómoda de hacerlo es añadir el script al package.json

El paquete typedoc-plugin-markdown genera la documentación en MarkDown, un formato cada vez más habitual en la generación e intercambio de documentos.


"docs:generate": "rm -rf docs && typedoc lib/ --plugin typedoc-plugin-markdown --out docs",

Creando el CHANGELOG.

Para crear un CHANGELOG de forma automática, incorporamos también un paquete que facilita la tarea con npm i -g auto-changelog —save-dev .

Adicionalmente tuneamos su configuración en el package.json para que incorpore todos los commits realizados y los ordene cronológicamente.


"auto-changelog": {

    "commitLimit": false,

    "unreleased": true,

    "sortCommits": "date-desc"

  }

Para aprovechar la generación de este fichero, modificamos el script npm anterior y creamos uno nuevo que también genere la documentación en HTML:


"docs:generate": "auto-changelog -p; rm -rf docs && typedoc lib/ --plugin typedoc-plugin-markdown --out docs && git add CHANGELOG.md",

"docs:generate:html": "auto-changelog -p; rm -rf docs-html && typedoc lib/ --plugin none --out docs-html && git add CHANGELOG.md"

La documentación se genera en los directorios docs y docs-html . Evitamos subirlo al repositorio actualizando .gitignore .

Tras realizar un par de actualización para acabar esta guía y dejar el paquete typescript-boilerplate listo en mi cuenta de GitHub me he dado cuenta de que es útil incorporar un script más al package.json


"docs:update": "npm run docs:generate && git commit -m \"📝 update docs\"",

Así quedó el directorio del proyecto

estructura definitiva de proyecto

Ahora te toca a tí…

Si has realizado el proyecto, ¿te ha bastado con seguir los pasos o se me olvidó incorporar alguno? ¿Se te ocurre una forma mejor de crear y configurar este boilerplate?

Publicado el 07/07/2022 por

¿Quieres más? te invitamos a suscribirte a nuestro boletín para avisarte cada vez que recopilemos contenido de calidad que compartir.

Si disfrutas leyendo nuestro blog, ¿imaginas lo divertido que sería trabajar con nosotros? ¿te gustaría?

Impulsamos el crecimiento profesional de tu equipo de developers