45

While building my Angular 6 application, I need to specify 2 things at once:

  • If it's production or development build
  • The locale I'm using

In my angular.json I have:

"build": {
  ...
  "configurations": {
    "production": {
      "fileReplacements": [
        {
          "replace": "src/environments/environment.ts",
          "with": "src/environments/environment.prod.ts"
        }
      ],
      "optimization": true,
      "outputHashing": "all",
      "sourceMap": false,
      "extractCss": true,
      "namedChunks": false,
      "aot": true,
      "extractLicenses": true,
      "vendorChunk": false,
      "buildOptimizer": true
    },
    "pl": {
      "fileReplacements": [
        {
          "replace": "src/assets/i18n/translations.json",
          "with": "src/assets/i18n/pl.json"
        }
      ]
    },
    "en": {
      "fileReplacements": [
        {
          "replace": "src/assets/i18n/translations.json",
          "with": "src/assets/i18n/en.json"
        }
      ]
    }
  }
}

But when I'm doing ng build --configuration=en --configuration=production I'm getting an error Configuration 'en,production' could not be found. I understand it means you can specify only 1 configuration at a time.

This means I need to create separate en, pl, productionEn, productionPl configurations. Though not the cleanest pattern, I can live with that.

"build": {
  ...
  "configurations": {
    "production": {
      "fileReplacements": [
        {
          "replace": "src/environments/environment.ts",
          "with": "src/environments/environment.prod.ts"
        }
      ],
      "optimization": true,
      "outputHashing": "all",
      "sourceMap": false,
      "extractCss": true,
      "namedChunks": false,
      "aot": true,
      "extractLicenses": true,
      "vendorChunk": false,
      "buildOptimizer": true
    },
    "pl": {
      "fileReplacements": [
        {
          "replace": "src/assets/i18n/translations.json",
          "with": "src/assets/i18n/pl.json"
        }
      ]
    },
    "en": {
      "fileReplacements": [
        {
          "replace": "src/assets/i18n/translations.json",
          "with": "src/assets/i18n/en.json"
        }
      ]
    },
    "productionPl": {
      "fileReplacements": [
        {
          "replace": "src/environments/environment.ts",
          "with": "src/environments/environment.prod.ts"
        },
        {
          "replace": "src/assets/i18n/translations.json",
          "with": "src/assets/i18n/pl.json"
        }
      ],
      "optimization": true,
      "outputHashing": "all",
      "sourceMap": false,
      "extractCss": true,
      "namedChunks": false,
      "aot": true,
      "extractLicenses": true,
      "vendorChunk": false,
      "buildOptimizer": true
    },
    "productionEn": {
      "fileReplacements": [
        {
          "replace": "src/environments/environment.ts",
          "with": "src/environments/environment.prod.ts"
        },
        {
          "replace": "src/assets/i18n/translations.json",
          "with": "src/assets/i18n/en.json"
        }
      ],
      "optimization": true,
      "outputHashing": "all",
      "sourceMap": false,
      "extractCss": true,
      "namedChunks": false,
      "aot": true,
      "extractLicenses": true,
      "vendorChunk": false,
      "buildOptimizer": true
    }
  }
}

But what I can't live with is copying and pasting the whole production configuration contents into productionEn and productionPl. If I add even more locales, or some third separate aspect that I'd like to specify during build, this pattern would become a total nightmare to maintain. Unfortunately it seem it's the pattern that Angular team recommends in their documentation.

Is there a way to tell Angular CLI that productionEn extends production, so to not duplicate the same configuration code multiple times? Something like the code below:

"build": {
  ...
  "configurations": {
    "production": {
      (...)
    },
    "pl": {
      "extends": "production",
      (...)
    },
    "en": {
      "extends": "production",
      (...)
    }
  }
}
Robert Kusznier
  • 6,471
  • 9
  • 50
  • 71
  • can't you simply omit the values equals? I think that you can only add the keys for "outputPath", "i18nFile","i18nFormat","i18nLocale" and "i18nMissingTranslation" but i'm not sure – Eliseo Aug 28 '18 at 19:03
  • @Eliseo I don't understand your point. As I can set only 1 configuration during the build, I need to specify all the values; don't see what I can omit. – Robert Kusznier Aug 29 '18 at 07:31
  • 1
    Maybe you have got a spelling mistake? `Configuration 'en,productions' could not be found` but your configurations name is `production` without s. – Felix Lemke Aug 29 '18 at 07:55
  • @ngfelixl: Good catch, but unfortunately it was just a typo I must have made when editing the post. I tested it again and it doesn't work, producing the error with `en,production` not `en,productions`. I corrected the post. – Robert Kusznier Aug 29 '18 at 07:58

4 Answers4

51

There finally is a way to do it, specifying multiple configurations in the command line:

ng build --configuration=en,production

Relevant issue in Angular repo

Note that --prod flag is ignored when you use --configuration (so you need to add production to the configuration list explicitly).

Angular docs for --configuration=configuration:

A named build target, as specified in the "configurations" section of angular.json. Each named target is accompanied by a configuration of option defaults for that target. Setting this explicitly overrides the "--prod" flag

Aliases: -c

Robert Kusznier
  • 6,471
  • 9
  • 50
  • 71
  • 1
    Nice one! THX! :) – mr.alex Nov 14 '19 at 12:36
  • 2
    Do you know which version of angular this is available from ? I currently use angular core 8.2.14 and angular cli 8.3.20 and can’t get it working. – ArchiFloyd Dec 16 '19 at 14:27
  • @ArchiFloyd Release history shows it available in [`9.0.0-next.10`](https://github.com/angular/angular-cli/releases/tag/v9.0.0-next.10). I'm not sure what exactly `-next.10` means, but it seems it'll be available only in `9.0.0`. – Robert Kusznier Dec 16 '19 at 22:38
  • 1
    I wonder how this work with say: `ng serve --configuration=en,myenv` It says: `Configuration 'en,myenv' is not set in the workspace.` – Agoun Feb 26 '20 at 22:25
  • 1
    @Agoun: Are you sure you're using the version in which it's supported? Sounds to me like you're still using an old one, where the syntax isn't supported, and so comma is interpreted as part of the configuration name. – Robert Kusznier Feb 26 '20 at 23:57
  • @RobertKusznier: this is probably the case, haven't upgraded yet (steps to be taken need extra attention and code updates for i18n support). Thank you Robert! – Agoun Feb 27 '20 at 05:06
  • I had trouble getting this to work correctly with `serve`. I found I had to do it with browser-target instead, like `ng serve myapp --browser-target=myapp:build:local,en-US` or `ng serve myapp --browser-target=myapp:build:local,es-MX`. This would use both my "local" configuration and the right culture's config together. See [this](https://github.com/angular/angular-cli/issues/17473#issuecomment-641078028). – Roobot Jun 15 '21 at 23:12
9

Update: see accepted answer for building with multiple configurations. The details below are now outdated


Reading through some issues and angular.json documentation, it appears that the options act as the defaults for the project

"architect": {
        "build": {          
          "options": {...}

These are overridden with partial options set in the configurations. From the Angular CLI workspace wiki:

configurations (object): A map of alternative target options.
configurationName (object): Partial options override for this builder.

This issue comment also mentions using configurations as an override

This sounds like all of the defaults for the project can be added to the options object e.g. move any duplicates from production, productionPl to the options: {}, and then add the fileReplacements, and the few other overrides that you require

Note: I have not tested this yet, it's just a suggestion based on the docs and issues

Drenai
  • 11,315
  • 9
  • 48
  • 82
  • 1
    That's a lot of info I didn't get from the documentation, thanks! For a moment I thought it'd solve this issue, but I realized one problem. Moving common options from `build.configurations.production` into `build.options` would mean that every single `ng build` would be a production build. That's undesired, as `ng build` is also invoked for example by `ng serve` (it's defined in `serve.browserTarget` option in `angular.json`) and those builds need not do all the processing that production builds do. So `production` mode definitely needs to be switchable on and off and not the default one. – Robert Kusznier Aug 29 '18 at 09:48
  • @RobertKusznier There are a lot of options that are identical between all those configurations, so you could move these up to the defaults couldn't you? That's all an `extends` feature would do in any case – Drenai Aug 29 '18 at 10:21
  • But if I move them to `options`, they would be also applied when running development server (as it's also running `ng build`, just without `--prod`/`--configuration=production`). That's completely undesired. Do you know how to handle/fix that? – Robert Kusznier Aug 29 '18 at 10:31
  • Note that these identical options that you mention should be applied only for production builds. – Robert Kusznier Aug 29 '18 at 10:38
  • 2
    @RobertKusznier From what I can see, apart from adding values to the default options, and overriding them in the configurations, there are no further CLI only solutions. Really what your looking for is an `extends`, which is a feature request (but I imagine that's way down the line: see `priority 3 nice to have` https://github.com/angular/angular-cli/issues/10612 ) – Drenai Aug 29 '18 at 11:18
  • 1
    So it's a feature request! Reading those issues I'm now convinced that what I ask for is simply impossible to achieve so far. Thanks for providing all those useful resources :). – Robert Kusznier Aug 29 '18 at 12:07
4

But this doesn't work for ng serve you say?

Here's what probably happened:

First, here's my configuration for angular12/architect/build/configurations:

"development": {

    "customWebpackConfig": {
       "path": "custom-webpack.dev.config.js",
       "replaceDuplicatePlugins": true
    },

    "buildOptimizer": false,
    "optimization": false,
    "vendorChunk": true,
    "extractLicenses": false,
    "sourceMap": false,
    "namedChunks": true,
    "aot": true
},

"quick": {

    "fileReplacements": [
        {
            "replace": "src/app/app-routing.module.ts",
            "with": "src/app/app-routing.module.quick.ts"
        }
    ]
}

I have a standard development config, with an added configuration called quick which is what I want to set in addition to the options from development.

(So my quick is the same as OP's en and pl.)

Sorry this isn't a magic new 'quick Angular build' feature (!) - I simply made a copy of my app-routing file, commented out every lazy module except the one I'm currently working with and configured the fileReplacements option to override the standard file. The build is significant faster for a large project. Clearly though I didn't want to accidentally deploy this which is why I needed a separate configuration.

What happens when you run ng serve --configuration development,quick is it looks in the serve part of the configuration (below). As you can see I added quick here as a browser target, referencing what I have above.

"serve": 
{
    "builder": "@angular-builders/custom-webpack:dev-server",

    "options": {
       "browserTarget": "angular12:build"
    },

    "configurations": 
    {
       "production": {
          "browserTarget": "angular12:build:production"
       },
       "development": {
          "browserTarget": "angular12:build:development"
       },
       "quick": {
          "browserTarget": "angular12:build:quick"
       }
    },
    "defaultConfiguration": "development"
},

What's actually happening when you ask for --configuration development,quick is it merges these two nodes:

"development": {
   "browserTarget": "angular12:build:development"
}

"quick": {
   "browserTarget": "angular12:build:quick"
}

Which results in:

"development": {
   "browserTarget": "angular12:build:development"
}

Makes sense why it didn't work now :-)

Solution:

The solution is fortunately as simple as updating serve/quick to:

"quick": {
     "browserTarget": "angular12:build:development,quick"
}

Then simply run:

ng-serve --configuration quick

This will in turn will merge your development and quick configurations under architect/build as if you were running ng build.


PS. You may see I'm using customWebpackConfig. That is the @angular-builders/custom-webpack package that allows for customization. It's irrelevant to my answer, but allowed me to prove to myself that it was indeed working as I was able to observe both my customWebpack plugins running and only the single lazy chunk I wanted was built.

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • 1
    There's another way which I found after I discovered this. See https://github.com/angular/angular-cli/issues/17473. You can directly use `browser-target` like this: `ng serve --browser-target angular12:build:development,quick`. It's effectively doing the same thing, but I prefer my way. It's quicker ;-) – Simon_Weaver Jun 01 '21 at 23:03
-3

are you ask about ?

"scripts": {
    [...]
    "build-i18n": "for lang in en es fr pt; 
       do ng build --config=$lang;
       done"
 }
Eliseo
  • 50,109
  • 4
  • 29
  • 67
  • 2
    It's not my question how to build all languages at once. Rather than that I'm asking how to build a specific language, let's say `en`, with `production` configuration? Something like `ng build --configuration=en --configuration=production`, but that doesn't work. – Robert Kusznier Aug 29 '18 at 07:54