Skip to content

Instantly share code, notes, and snippets.

@mugan86
Forked from anartzdev/article.md
Last active March 13, 2024 06:58
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save mugan86/b911a73ff98b0b2c72e7e790ce4807db to your computer and use it in GitHub Desktop.
Save mugan86/b911a73ff98b0b2c72e7e790ce4807db to your computer and use it in GitHub Desktop.
Article information - ESLint + Prettier + Husky + Gitflow in Angular

El objetivo principal de este artículo es proporcionar los pasos a seguir con las instrucciones detalladas y necesarias para poder configurar un proyecto de Angular con las herramientas que nos va a ayudar a trabajar con buenas prácticas.

Estas buenas prácticas que queremos aplicar se realizarán tanto en la escritura del código, siguiendo una línea estable de escritura con unas normas preestablecidas y también en lo que respecta a la adición de mensajes de commit donde realizaremos las configuraciones necesarias con el objetivo de respetar la convención para escribir los mensajes de commit siguiendo una estructura común que se usará en infinidad de proyectos, con lo que esto proporcionará una forma de trabajar estable y correcta mediante git flow escribiendo mensajes de commit.

Preparativos para empezar a trabajar

El tutorial se desarrollará con una aplicación Angular en la versión 13 (es igual de aplicable para la versión 14) y lo podéis descargar desde aquí, para no tener que estar creando el proyecto de 0 y así poder empezar en el mismo punto del que me encuentro yo actualmente.

¿Qué pasos vamos a dar en esta guía?

Los pasos que vamos a dar para configurar nuestro proyecto de Angular aplicando una estructura y funcionalidad que nos permitirá aplicar buenas prácticas son los siguientes:

  1. ESLint: Linter de Javascript que nos va a permitir hacer que compruebe mediante unas normas preestablecidas, que se cumplen a la hora de codificar en el proyecto.
  2. Husky: Herramienta que es un paquete que sirve para controlar el uso de los Hooks de Git de una manera super fácil sin mucho esfuerzo. Instalación y primeras configuraciones con el hook pre-commit.
  3. CommitLint: es un paquete que nos puede ayudar a mantener los mensajes de la historia de git con un estándar respetando una estructura que ya se mencionará posteriormente. Aquí configuraremos el hook commit-msg para comprobar que seguimos las buenas prácticas mediante la ejecución de commitlint
  4. Prettier: es una** herramienta para formatear el código**, tiene soporte para HMTL, CSS, JavaScript, JSX, TypeScript, GraphQL… Lo usaremos para formatear principalmente el código HTML, CSS y Typescript que encontraremos en nuestro proyecto de Angular añadiendo unas propiedades como espacio para tabulaciones, si usamos ";", comillas simples y demás.

Ahora que ya sabemos que pasos vamos a seguir en este artículo para configurar todo el pack de herramientas para trabajar con buenas prácticas en nuestro proyecto, procedemos a completar los pasos uno por uno hasta llegar a la meta donde podremos formatear el código automáticamente con prettier usando el linter de eslint mientras comprobamos si los mensajes de los commit aplican las recomendaciones para los mensajes de commit usando la herramienta commitlint.

1.- Configurar ESLint

Para configurar ESLint en Angular vamos a seguir las instrucciones que podremos encontrar en este enlace que os proporciono. Esto será compatibles con las versiones de Angular 12+. Accedemos a el para tener toda la información y ver otros aspectos que nos pueda resultar interesante. Ahora que estamos dentro del README, seguimos los pasos principales para configurar correctamente ESLint en el proyecto de Angular.

  • Paso 1 y 2 del proceso a seguir completados, ya que el proyecto ya lo tenemos creado y operativo. Si no lo habéis realizado, os recomiendo que lo descarguéis desde aquí.
  • Debemos de ejecutar el siguiente comando de schematics: ng add @angular-eslint/schematics Esto servirá para que automáticamente instale las dependencias necesarias y a su vez realice las configuraciones necesarias como añadir en angular.json el registro de los diferentes Schematics usados en la propiedad "schematicCollections" entre otros ajustes.

Al ejecutar el comando nos preguntarán si queremos seguir hacia adelante y decimos que si (Y) enter image description here Cuando pase un instante y haga los cambios en los diferentes ficheros, al terminar nos muestra este feedback: enter image description here Ahora vamos mirando que ficheros y modificaciones se han realizado que se pueden ver a continuación: enter image description here

Vamos fichero por fichero analizando los cambios:

  • .eslintrc.json: fichero con las reglas del linter que usaremos para comprobar si el código cumple esas normas y darnos feedback mediante advertencias y errores para solucionarlo. El contenido por defecto es el siguiente:
{
  "root": true,
  "ignorePatterns": [
    "projects/**/*"
  ],
  "overrides": [
    {
      "files": [
        "*.ts"
      ],
      "parserOptions": {
        "project": [
          "tsconfig.json"
        ],
        "createDefaultProgram": true
      },
      "extends": [
        "plugin:@angular-eslint/recommended",
        "plugin:@angular-eslint/template/process-inline-templates"
      ],
      "rules": {
        "@angular-eslint/directive-selector": [
          "error",
          {
            "type": "attribute",
            "prefix": "app",
            "style": "camelCase"
          }
        ],
        "@angular-eslint/component-selector": [
          "error",
          {
            "type": "element",
            "prefix": "app",
            "style": "kebab-case"
          }
        ]
      }
    },
    {
      "files": [
        "*.html"
      ],
      "extends": [
        "plugin:@angular-eslint/template/recommended"
      ],
      "rules": {
        
      }
    }
  ]
}
  • angular.json: Fichero que se modifica para añadir en el registro de las colecciones de schematics la información del schematics que acabamos de instalar. Se añaden esos cambios hacia el final del fichero.
{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "AngularHuskyWithGitHooks": { // Nombre del proyecto
      "projectType": "application",
      "schematics": {
        ...
      },
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          ...          
        },
        "serve": {
          ...
        },
        "extract-i18n": {
         ...
        },
        "test": {
          ...
        },
        // "lint se añade con las configuraciones con el patrón de los ficheros a analizar
        "lint": {
          "builder": "@angular-eslint/builder:lint",
          "options": {
            "lintFilePatterns": [
              "src/**/*.ts",
              "src/**/*.html"
            ]
          }
        }
      }
    }
  },
  "defaultProject": "AngularHuskyWithGitHooks",
  // Se añade el registro del schematics en cli
  "cli": {
    "defaultCollection": "@angular-eslint/schematics"
  }
}
  • package.json: Cambio en el script donde se añade un nuevo script llamado "lint" que servirá para hacer el escaneo y analizar que los ficheros pasan las reglas establecidas por defecto. El comando es el siguiente: ng lint Los cambios se reflejan en el package.json de esta manera: enter image description here

Llegados a este punto ya tenemos el primer paso dado, podemos probar ejecutando el linter con yarn lint (o npm run lint si no usáis yarn) aunque en estos momentos no tiene mucha relevancia. Podemos ver todos los cambios en este commit. Seguimos al segundo paso.

2.- Instalación y configuración de Husky

Para instalar Husky en nuestro proyecto lo primero vamos a acceder a este enlace para ver las diferentes opciones de instalación que son las siguientes:

npx husky-init && npm install         # Si trabajamos con NPM
npx husky-init && yarn                # Si trabajamos con Yarn 1 (este es mi caso)
yarn dlx husky-init --yarn2 && yarn   # Si trabajamos con Yarn 2+
pnpm dlx husky-init && pnpm install   # Si trabajamos con pnpm

Dependiendo de como gestionemos nuestras dependencias debemos de ejecutar la instalación de una manera u otra. Lo más normal es que estéis trabajando con NPM o Yarn 1 pero sea cual sea vuestra manera de gestionar las dependencias deberéis de realizarlo tal y como se indica.

En el caso de mi equipo estoy usando Yarn 1 para gestionar las dependencias y por eso ejecutaré el siguiente comando:

npx husky-init && yarn

Al ejecutar nos debe de mostrar algo de este estilo: Husky Install process

Con esto se añadirá un directorio en la raíz llamado .husky que contiene un fichero para ejecutar los hooks de Git llamado pre-commit con el comando para ejecutar el test de Angular con npm test

Configurando el Hook de Git llamado pre-commit en Husky

.husky/pre-commit

Husky Pre-commit initial

Lo que hace básicamente es accede dentro del fichero Shell de husky en nuestra máquina y ejecuta cuando detecta la acción previa al commit, ejecuta npm test que se asigna automáticamente cuando hemos instalado y realizado las primeras configuraciones de Husky.

En este punto podemos añadir más comandos como la ejecución de los test unitarios, los test de integración o incluso el plugin de prettier para dar formato al código siguiendo una normas establecidas. Esto último, como bien se sabe, lo haremos. Vamos paso por paso primero trabajando con el Linter de ESLint.

Se modifica el package.json añadiendo el apartado de Husky automáticamente cuando hemos ejecutado el comando para instalar y configurar Husky automáticamente.

package update with Husky

Ahora accedemos al fichero pre-commit dentro del directorio .husky y modificamos el contenido reemplazando con yarn lint (npm run lint si estamos con NPM). Es decir, eliminamos npm test y lo sustituimos.

Yarn Lint in Hook pre-commit

Para asegurarnos que funciona todo correctamente añadimos todos los permisos para ejecutar correctamente chmod a+x .husky/pre-commit para evitar problemas cuando estamos ejecutando los Hooks de Git.

Ya tenemos definida la acción para ejecutar automáticamente yarn lint mediante hooks, con el hook pre-commit. Esto se ejecutará siempre y cuando estemos realizando un commit habilitando que este hook se ejecute antes de realizar la anotación en el registro (que podemos comprobar con git log).

Ahora pueden ocurrir dos situaciones:

  • ERROR en linter: No se añadirá el commit que queremos añadir con git commit -m "feat: mensaje de prueba" si da un ejemplo como el siguiente (voy a modificar un fichero para forzar el error cambiando el prefijo de la aplicación principal por anartz teniendo que ser app ya que está establecido en las reglas en el fichero .eslintrc.json

ESLint component selector rule

Haciendo el cambio en el selector de app-root a anartz-root forzaremos el error al ejecutar yarn lint Change selector

Este será el error que se mostrará con ese desajuste:

Error lint when add incorrect selector

  • OK en linter: Se añadirá el commit que hemos especificado y podremos consultarlo en el log de las diferentes anotaciones del registro de git.

Este sería el mensaje que se mostraría en el linter si todo está OK (volvemos a dejar el selector con el contenido inicial) que haría posible que se añada el commit.

Pass linter when add correct selector

Después de haber trabajado con el hook pre-commit, seguimos con el hook commit-msg que se ejecutará cuando ya se va a añadir el comentario del commit y hemos pasado la ejecución del pre-commit y todo está OK.

Para respetar el formato de los mensajes siguiendo lo descrito por la convención de git flow, para escribir mensajes de commit vamos a instalar commitlint que es un linter que comprueba la estructura de los mensajes y sigue estas normas principales

Comenzamos con las configuraciones siguiendo la siguiente referencia: https://commitlint.js.org/#/guides-local-setup

Ejecutamos el siguiente comando, donde instalamos @commitlint/cli y @commitlint/config-conventional

yarn add -D @commitlint/{cli,config-conventional}

Este sería el resultado que se muestra al ejecutar la instalación: Commitlint Install dependencies process

Configuramos commitlint usando la configuración convencional mediante la creación del fichero commitlint.config.js ejecutando el siguiente comando:

echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js

Al ejecutar el comando, en la raíz del proyecto veremos un nuevo fichero commitlint.config.js.

Commit Lint rules configuration

Añadimos el Hook de Git llamado commit-msg en Husky

.husky/commit-msg

Para crear el fichero con el comando que se ejecutará para lintear el commit lo realizamos ejecutando el siguiente comando:

npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'

Y con ello conseguimos que se cree el fichero con este contenido: Commit-msg hook with apply commitlint

Añadimos chmod a+x .husky/commit-msg para gestionar y otorgar todos los permisos en este fichero para evitar problemas cuando ejecutemos mediante Git Hooks el hook configurado commit-msg.

Ahora sin ejecutar el Hook podemos hacer pruebas "a mano" ejecutando el comando para utilizar el Linter de los commits y lo podemos hacer de la siguiente manera (basándonos en lo siguiente):

Manually test if commit message use correct format

¿Cuál es el motivo de que nos de un error? Como bien nos indica el feedback del error, el tipo debe de ser uno de los especificados en el mensaje "type must be one of [build, chore,..." y como se puede observar, el type está especificado con "foo".

Para saber como debemos de formar los mensajes respetando una estructura y normas sencillas tenemos que tener en cuenta lo siguiente:

feat: lectura de código QR para extraer datos de contacto
^--^  ^------------^
|     |
|     +-> Mensaje de commit (tiene que empezar en minúsculas).
|
+-------> Type: chore, docs, feat, fix, refactor, style, ó test.

Propósito de cada tipo:

  • feat: (una nueva feature (funcionalidad) del usuario, no será cuando hacemos un build)
  • fix: (solución a un bug surgido en el código, no será cuando hacemos build)
  • docs: (cambios en la documentación)
  • style: (formateo, añadir semicolons, etc; sin cambios en el código de producción)
  • refactor: (refactorizar código de producción, ej. renombar una variable)
  • test: (añadir test que faltan, refactorizar tests; sin cambios en el código de producción)
  • chore: (actualizar tareas grunt, etc; sin cambios en el código de producción)

Recordad que usaremos esta referencia para añadir correctamente los mensaje de commit siguiendo la guía de estilo convencional de escritura de commit de Git: https://www.conventionalcommits.org/en/v1.0.0/

Con esto ya tenemos todo lo necesario configurado para trabajar con los hooks pre-commit y commit-msg, vamos a probarlos ejecutando un commit para los cambios realizados.

Ejecutamos el siguiente comando:

git commit -m "chore: add config pre-commit and commit-msg hooks"

Al ejecutar el comando pasará por dos estados:

  • pre-commit: Comprueba el linter, si es correcto, que en teoría lo es ya que no hemos cambiado código.
  • commit-msg: Comprueba el mensaje del commit que en este caso usa el formato correcto (por si acaso, siempre podemos ejecutar echo 'chore: add config pre-commit and commit-msg hooks' | npx commitlint para hacer una prueba.

Creamos el commit y este será el resultado Result execute Git commit and use hooks

Como en el commit, al ejecutar pre-commit todo ok y el commit-msg cuyo mensaje respeta las normas ya tenemos todo OK. Con esto ya hemos terminado este paso y pasamos al último donde vamos a aprender a configurar Prettier para darle formato automáticamente al código cuando hacemos un commit con las mismas reglas, sea quien sea el que escribe el código.

Los cambios realizados en este apartado con la configuración e instalación de Husky con los Hooks de Git los podemos encontrar en este enlace.

Para dar más cobertura de información sobre estos temas, os invito a consultar estas referencias:

Más información sobre los Git Hooks: https://git-scm.com/docs/githooks

Añadir configuración de Prettier con ESLint para formatear el código

Ahora que ya tenemos la verificación del linter con los ajustes básicos para comprobar que el código sigue unas normas, vamos a añadir el plugin Prettier combinándolo con ESLint para que corrija lo que no está correcta en las reglas de ESLint junto con las que vamos a añadir en la configurar del Prettier.

¿Cuál es el objetivo de ir añadiendo estas herramientas? El objetivo principal es que se siga una línea de desarrollo a la hora de escribir, sobre todo cuando estamos en equipos de varias personas, para que lo escrito se amolde a lo que se ha definido y así seguir una línea en común.

Para realizar el proceso de configuración seguiremos los siguientes pasos:

1.- Instalar Prettier en desarrollo:

Ejecutamos el siguiente comando donde añadimos prettier junto con prettier-quick que se encargará de escribir el contenido de los ficheros del proyecto de Angular donde tenemos los componentes, directivas, pipes,...

yarn add -D prettier pretty-quick

Una vez instaladas las dependencias mencionadas, seguimos hacia adelante

2.- Añadir los ficheros .prettierrc.json y .prettierignore en la raíz del proyecto.

Creamos el fichero .prettier.json y añadimos el siguiente contenido donde vamos a especificar las normas que queremos añadir en el apartado de configuración para este proyecto con Prettier:

{ 
    "tabWidth": 2, 
    "useTabs": false, 
    "singleQuote": true, 
    "semi": true, 
    "bracketSpacing": true, 
    "arrowParens": "avoid", 
    "trailingComma": "es5", 
    "bracketSameLine": true, 
    "printWidth": 80 
}

Añadimos el fichero .prettierignore en el que vamos a añadir lo mismo que en el fichero .gitignore

Los ficheros creados aparecerán de la siguiente manera:

prettier files

3.- Configurar prettier para trabajar con ESLint

Añadimos el siguiente comando para instalar las dependencias necesarias y lo hacemos en desarrollo:

yarn add prettier-eslint eslint-config-prettier eslint-plugin-prettier -D

4.- Editar .eslintrc.json

Pasamos del contenido que tenemos actualmente.

{
  "root": true,
  "ignorePatterns": [
    "projects/**/*"
  ],
  "overrides": [
    {
      "files": [
        "*.ts"
      ],
      "parserOptions": {
        ...
      },
      "extends": [
        "plugin:@angular-eslint/recommended",
        "plugin:@angular-eslint/template/process-inline-templates"
        <====== AQUí añadimos el plugin de prettier
      ],
      "rules": {
        ...      }
    },
    {
      "files": [
        "*.html"
      ],
      "extends": [
        "plugin:@angular-eslint/template/recommended"
      ],
      "rules": {
        
      }
    }
    <===== AQUI AÑADIMOS UNA NUEVA CONFIG
  ]
}

Quedando los cambios establecidos en los puntos mencionados:

{
  "root": true,
  "ignorePatterns": [
    "projects/**/*"
  ],
  "overrides": [
    {
      "files": [
        "*.ts"
      ],
      "parserOptions": {
        ...
      },
      "extends": [
        "plugin:@angular-eslint/recommended",
        "plugin:@angular-eslint/template/process-inline-templates"
        // Prettier
        "plugin:prettier/recommended"
      ],
      "rules": {
        ...      }
    },
    {
      "files": [
        "*.html"
      ],
      "extends": [
        "plugin:@angular-eslint/template/recommended"
      ],
      "rules": {
        
      }
    },
    // Nueva configuración
    {
      "files": ["*.html"],
      "excludedFiles": ["*inline-template-*.component.html"],
      "extends": ["plugin:prettier/recommended"],
      "rules": {
        // NOTE: WE ARE OVERRIDING THE DEFAULT CONFIG TO ALWAYS SET THE PARSER TO ANGULAR (SEE BELOW)
        "prettier/prettier": ["error", { "parser": "angular" }]
      }
    }
  ]
}

Si queréis ver los cambios con más detalle y ante cualquier duda, podéis acceder a este enlace

5.- Añadir en la configuración de .vscode un nuevo fichero llamado settings.json con el siguiente contenido para establecer el plugin prettier a usar tanto en HTML y Typescript, necesario en este proyecto:

{
 "[html]": {
 "editor.defaultFormatter": "esbenp.prettier-vscode",
 "editor.codeActionsOnSave": { "source.fixAll.eslint": true },
 "editor.formatOnSave": false
 },
 "[typescript]": {
 "editor.defaultFormatter": "dbaeumer.vscode-eslint",
 "editor.codeActionsOnSave": { "source.fixAll.eslint": true },
 "editor.formatOnSave": false
 }
}

6.- Añadir nuevo script para limpiar los apartados que detecta eslint con prettier

"lint:fix": "ng lint --fix",

En el package.json se reflejará de la siguiente manera:

prettier files

7.- Modificar el hook pre-commit

Añadimos el siguiente comando npx pretty-quick --staged && yarn lint:fix en los scripts del package.json, quedando de la siguiente manera: prettier files

Ahora, en el fichero que hemos creado anteriormente con este contenido: .husky/pre-commit

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

yarn lint

Y lo modificamos, dejándolo de la siguiente manera: .husky/pre-commit

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

yarn prettier

8.- Ejecutar commit haciendo un check de todos los ficheros

prettier files Todo lo desarrollado paso a paso lo podemos encontrar en este resultado final: https://github.com/mugan86/angular-husky-eslint-gitflow-commit-prettier

Podemos usar este repositorio como un template para iniciar nuestros proyectos con toda esta configuración, algo que nos va a reducir un montón de trabajo.

Espero que os sea de gusto el artículo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment