angularsassangular-cli

How to replace SCSS files with Angular CLI?


Angular CLI provides the possibility to replace files when building the project. I would like to use this functionality to replace a SCSS file with default styling, with a SCSS file that contains customer specific CSS.

But Angular CLI seems not to replace the SCSS files. How could I achieve this?


What I have tried:

environment.default.ts

export const environment = {
  name: 'default'
};

environment.special.ts

export const environment = {
  name: 'special'
};

default/_scss-config.scss

@debug ('default'); // gets called but should never be called

special/_scss-config.scss

@debug ('special'); // never gets called

angular.json (only relevant parts)

  "architect": {
    "build": {
      "builder": "@angular-devkit/build-angular:browser",
      "options": {
        "fileReplacements": [
          {
            "replace": "src/environments/environment.default.ts",
            "with": "src/environments/environment.special.ts"
          },
          {
            "replace": "src/scss/default/_scss-config.scss",
            "with": "src/scss/special/_scss-config.scss"
          }
        ],
        "stylePreprocessorOptions": {
          "includePaths": [
            "src/scss/default"
          ]
        }
      }
    }
  }

main.ts

import { environment } from './environments/environment.default';

console.log(environment.name); // 'special' --> replacement works!

Result:
File replacement for .ts files works, but not for .scss files, as the special debug statment never gets called, but the default does, even the file should have been repalced.


Solution

  • Angular.io states that:

    The main CLI configuration file, angular.json, [...] allows you to replace any file with a target-specific version of that file.

    This is in fact the vision, but according to this GitHub thread, this is not true for now.
    It seems up till now, only .ts and .html files are supported.

    Therefore, currently the answer to the question is no, it is yet not possible to replace SCSS files.

    But there is a workaround (see below).


    Update Jan-2021: Still doesn't work. Tested with Angular 11.0.9.

    TLDR:

    Background:
    It seems the inital statement in the documentaion to support all file types was wrong. With Angular 11 a check was introduced, to error on unsupported file types.

    fix(@angular-devkit/build-angular): add validation to fileReplacement…

    fileReplacement is meant to replace compilation source files (JavaScript or TypeScript) with other compilation source files in the build. With this change we add validation to fail the build when the files have unsupported extensions.

    So with Angular 11 two things changed. At first you have to use src and replaceWith instead of replace and with. Though this doesn't help as you will get Data path ".fileReplacements[1].replace" should match pattern "\.(([cm]?j|t)sx?|json)$"., if you use unsupported file types like .scss.


    Update Feb-2020: Still doesn't work. Tested with Angular 9.0.0.


    But there is a workaround:
    You can use different configurations in angular.json to achieve the same goal - replacing a SCSS file.

    folder structure:

    src/
        scss/
            default/
                _scss-config.scss
            special/
                 _scss-config.scss
    

    src/scss/default/scss-config.scss

    body { background-color: blue; }
    

    src/scss/special/scss-config.scss

    body { background-color: red; }
    

    angular.json

    <!-- language: lang-json -->
    
    {
      [...]
      "architect": {
        "build": {
          "options": {
            "stylePreprocessorOptions": {
              "includePaths": [
                "src/scss/default"
              ]
            }
          },
          "configurations": {
            "default": {
              "stylePreprocessorOptions": {
                "includePaths": [
                  "src/scss/default/"
                ]
              }
            },
            "special": {
              "stylePreprocessorOptions": {
                "includePaths": [
                  "src/scss/special/"
                ]
              }
            }
          }      
        },
        "serve": {
          "configurations": {
            "default": {
              "browserTarget": "angular-scss-replacement:build:default"
            },
            "special": {
              "browserTarget": "angular-scss-replacement:build:special"
            }
          }
        }     
        [...]      
      }
    }
    

    style.scss or component.scss

    @import 'scss-config';
    
    /* use your SCSS variables and stuff as you normally would */
    

    Note that you mustn't include the _ (underscore) from the filename in the import statement
    (for more details read the Partials section)

    How to use it:

    // 'ng serve'-command uses default configuration as defined in angular.json > build 
    ng serve
    
    // default config creates blue background
    ng serve --configuration=default
    
    
    // specail config create red background
    ng serve -c=special
    

    (This solution is from the thread mentioned above - thx to richardtreier)


    Drawbacks of this approach: