# Configurar y generar aplicaciones Angular para múltiples entornos personalizados

Cuando generamos una aplicación con Angular CLI, la plantilla inicial de los ficheros package.json, angular.json y environments.ts solo cuentan de manera predeterminada con la configuración para los entornos de desarrollo y producción, así que ¿cómo podemos extender esa configuración para poder definir nuevos entornos personalizados?

Para ello voy a plantear una serie de cambios sobre dichas plantillas con lo que finalmente tendremos la configuración para cuatro entornos de implementación:

  • default: Será el entorno predeterminado de ejecución local.
  • development: Será el entorno de desarrollo.
  • staging: Será el entorno de preproducción.
  • production: Será el entorno de producción.

# Extender la configuración de la plantilla inicial de package.json

Esta es la sección scripts de un fichero package.json estándar de una aplicación Angular:

,
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },,

package.json

Y esta sería la misma sección del fichero aplicando las modificaciones que propongo:

,
  "scripts": {
    "ng": "ng",
    "start": "ng serve -o",
    "start:development": "ng serve -o --configuration=development",
    "start:staging": "ng serve -o --configuration=staging",
    "start:production": "ng serve -o --configuration=production",
    "build": "ng build",
    "build:development": "ng build --configuration=development",
    "build:staging": "ng build --configuration=staging",
    "build:production": "ng build --configuration=production",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "stats": "ng build --stats-json & webpack-bundle-analyzer dist/my-app/stats.json"
  },,

package.json

Y cuyos cambios realizados han sido:

  • Se agregan tantos comandos start como entornos (start:development, start:staging, y start:production) con el parámetro --configuration establecido con el nombre de cada entorno (veremos su explicación cuando veamos el fichero angular.json).
  • Se agregan tantos comandos build como entornos (build:development, build:staging y build:production) también con el parámetro --configuration establecido con el nombre de cada entorno.
  • Al comando ng serve he añadido la opción --o, que aunque no sea relevante para la configuración por entorno, sí que nos hace más cómoda la ejecución de la aplicación al hacer que se abra automáticamente nuestro navegador predeterminado.
  • Y para terminar también he añadido como mejora al fichero, más allá de la configuración por entorno, el comando stats para el análisis de paquetes npm con webpack.

# Extender la configuración de la plantilla inicial de angular.json

Estas son las secciones architect.build.configurations y architect.serve de un fichero angular.json estándar de una aplicación Angular:

,
  "architect": {
    "build": {
      ...,
      "configurations": {
        "production": {
          "fileReplacements": [
            {
              "replace": "src/environments/environment.ts",
              "with": "src/environments/environment.prod.ts"
            }
          ],
          "optimization": true,
          "outputHashing": "all",
          "sourceMap": false,
          "namedChunks": false,
          "extractLicenses": true,
          "vendorChunk": false,
          "buildOptimizer": true,
          "budgets": [
            {
              "type": "initial",
              "maximumWarning": "500kb",
              "maximumError": "1mb"
            },
            {
              "type": "anyComponentStyle",
              "maximumWarning": "2kb",
              "maximumError": "4kb"
            }
          ]
        }
      }
    },
    "serve": {
      ...,
      "configurations": {
        "production": {
          "browserTarget": "my-app:build:production"
        }
      }
    },
    ...,
  },

angular.json

Y estas serían las mismas secciones del fichero aplicando las modificaciones que propongo:

,
  "architect": {
    "build": {
      ...,
      "configurations": {
        "development": {
          "fileReplacements": [
            {
              "replace": "src/environments/environment.ts",
              "with": "src/environments/environment.development.ts"
            }
          ],
          "optimization": true,
          "outputHashing": "all",
          "sourceMap": false,
          "namedChunks": false,
          "extractLicenses": true,
          "vendorChunk": false,
          "buildOptimizer": true,
          "budgets": [
            {
              "type": "initial",
              "maximumWarning": "500kb",
              "maximumError": "1mb"
            },
            {
              "type": "anyComponentStyle",
              "maximumWarning": "2kb",
              "maximumError": "4kb"
            }
          ]
        },
        "staging": {
          "fileReplacements": [
            {
              "replace": "src/environments/environment.ts",
              "with": "src/environments/environment.staging.ts"
            }
          ],
          "optimization": true,
          "outputHashing": "all",
          "sourceMap": false,
          "namedChunks": false,
          "extractLicenses": true,
          "vendorChunk": false,
          "buildOptimizer": true,
          "budgets": [
            {
              "type": "initial",
              "maximumWarning": "500kb",
              "maximumError": "1mb"
            },
            {
              "type": "anyComponentStyle",
              "maximumWarning": "2kb",
              "maximumError": "4kb"
            }
          ]
        },
        "production": {
          "fileReplacements": [
            {
              "replace": "src/environments/environment.ts",
              "with": "src/environments/environment.production.ts"
            }
          ],
          "optimization": true,
          "outputHashing": "all",
          "sourceMap": false,
          "namedChunks": false,
          "extractLicenses": true,
          "vendorChunk": false,
          "buildOptimizer": true,
          "budgets": [
            {
              "type": "initial",
              "maximumWarning": "500kb",
              "maximumError": "1mb"
            },
            {
              "type": "anyComponentStyle",
              "maximumWarning": "2kb",
              "maximumError": "4kb"
            }
          ]
        },
      }
    },
    "serve": {
      ...,
      "configurations": {
        "development": {
          "browserTarget": "my-app:build:development"
        },
        "staging": {
          "browserTarget": "my-app:build:staging"
        },
        "production": {
          "browserTarget": "my-app:build:production"
        }
      }
    },
    ...,
  },

angular.json

Y cuyos cambios realizados han sido:

  • Bajo la sección architect.build.configurations se agregan tantas configuraciones como entornos personalizados hayamos definido (el default usará la configuración por defecto de desarrollo de Angular, como el fichero environment.ts). Estas configuraciones deben corresponder con los nombres especificados en el parámetro --configuration de los comandos start y build personalizados que hemos creado en el fichero package.json. Como el fichero ya incluye la configuración production, sólo tenemos que crear las configuraciones development y staging.
  • En cada configuración creada bajo la sección architect.build.configurations, hemos personalizado los nombres de los ficheros de entorno (sección fileReplacements) que vamos a usar, en nuestro caso environment.development.ts, environment.staging.ts y environment.production.ts (hemos sustituido el nombre del fichero original environment.prod.ts para mantener la coherencia con los nuevos que hemos creado).
  • A la sección architect.serve hemos añadido los dos nuevos entornos que nos faltaban (devlopment y staging) dado que el de producción ya estaba creado.

# Extender la configuración de la plantilla inicial de environment.ts

Dado que ya contamos con los ficheros de configuración de entorno environment.ts y environment.prod.ts, lo único que tenemos que hacer es:

  • Crear los nuevos ficheros environment.development.ts y environment.staging.ts.
  • Modificar el nombre del fichero environment.prod.ts por environment.production.ts.

# Usar la configuración personalizada según entorno de ejecución

A partir de este momento ya podemos arrancar nuestra aplicación o empaquetarla para cada uno de los entornos definidos de la siguiente manera:

npm run start
npm run start:development
npm run start:staging
npm run start:production

npm run build
npm run build:development
npm run build:staging
npm run build:production

En el caso del comando start, podríamos obviar el comando run que le precede y simplificarlo con npm start ya que por sí mismo start es un comando de npm.