Angular-cli: fileReplacements doesn't work with css files

Created on 4 Jul 2018  路  44Comments  路  Source: angular/angular-cli

Bug Report or Feature Request (mark with an x)

- [x] bug report -> please search issues before submitting
- [ ] feature request

Area

- [x] devkit
- [ ] schematics

Versions


node --version: v9.4.0
npm --version: 5.6.0
macOS high sierra 10.13.5

Repro steps


1- In angular.json add a new configuration and try to replace a css file.
2- add a simple background style to header.local.css
3- import the header.css file into styles.scss

E.g.

"configurations": {
            "local": {              
              "optimization": false,
              "outputHashing": "all",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": false,
              "vendorChunk": false,
              "buildOptimizer": false,
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/enviroment.local.ts"
                },
                {
                  "replace": "src/css/header.css",
                  "with": "src/css/header.local.css"
                }
              ]
            },

The log given by the failure


none

Desired functionality


get the header.css
replaced by header.local.css

Mention any other details that might be useful


Tested with angular cli version 6.0.1 and angular cli version 6.1.0-beta.0

devkibuild-angular browser low broken triage #1 bufix

Most helpful comment

I am writing an app that is built for several clients.
Each clients have a variable.scss file with its Material Design variables
I wanted to use fileReplacements to switch this variable file at build time but it does not work.
I am pretty sure this is a common need, not sure how others do but fixing this issue would make the world a better place.
thanks

All 44 comments

As said in https://github.com/angular/angular-cli/issues/10881#issuecomment-389644466, ng-cli should be able to replace all type of files from 6.1.0, and not only .ts files.
This feature seems to be active in the rc but not in beta, see https://github.com/angular/angular-cli/issues/10881#issuecomment-399063501.

We added the generic replacement functionality in https://github.com/angular/devkit/pull/887, but this is limited to webpack loader and plugins that actually use the webpack file system. This might not work for all files, and we should error out when we know that's the case.

I have updated my angular/cli to version 6.1.1 and angular-devkit/build-angular to version 0.7.1.
I see ng-build started to replace html files. However it is still not replacing scss files.
My configuration looks like this:

```
"test": {
"fileReplacements": [
{
"replace": "src/index.html",
"with": "src/index_test.html"
},
{
"replace": "src/scss/base/_variables.scss",
"with": "src/scss/themes/_dark.scss"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": true,
"extractCss": true,
"namedChunks": false,
"aot": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": false
}
````

So I have updated angular/cli to version 6.1.2 but to no avail.
@filipesilva so far we know that it works with html, and ts files. It doesn't work (from what I have tested) with css, scss and json files.
What are the next steps to this issue?
Can we at least make sure that the basic files like css, html and ts get replaced so most use cases are covered?

@angular/[email protected]
@angular-devkit/[email protected]

OR

@angular/[email protected]
@angular-devkit/[email protected]

Replacing scss files still not working.

Any updates on this issue? Should it be fixed in 6.1.5?

Im trying to replace a constants.scss (with constants.newconfig.scss) file that I import at the top of my root styles file. But none of the newconfig styles are pulling through.

The strange thing is if I use this method with a component specific scss file it works perfectly. From what I can see its only with the root style files.

Here's a stackoverflow link to my question.

_Im using 6.1.5_

with @angular/cli: 6.2.1 I was able to switch the global style file, but not a variables file like this:

"fileReplacements": [
  {
    "replace": "projects/example/src/styles.scss",
    "with": "projects/example/src/styles-example.scss"
  }
]

However the latter would be a more lean solution.

I am writing an app that is built for several clients.
Each clients have a variable.scss file with its Material Design variables
I wanted to use fileReplacements to switch this variable file at build time but it does not work.
I am pretty sure this is a common need, not sure how others do but fixing this issue would make the world a better place.
thanks

@shprink My exact need as well. Holding my thumbs that these issues will be solved soon so we can use fileReplacements as you would with Android Build Flavors or Swifts Build Variants

@shprink same 馃憤

I created component "widget" and need some static demo for this component. It mean, that I need to change controller, but html and styles stay same. To do it I can use "fileReplacement" feature. But it doesn't work...

src/app/widget/widget.component.ts:

import {Component, OnInit} from '@angular/core';

@Component({
    selector: 'fh-widget',
    templateUrl: './widget.component.html',
    styleUrls: ['./widget.component.scss']
})
export class WidgetComponent implements OnInit {
    title = '';

    constructor() {
    }

    ngOnInit() {
        this.title = 'wodget - real component';
    }
}

src/app/widget/widget-static/widget.component.ts

import {Component, OnInit} from '@angular/core';

@Component({
    selector: 'fh-widget',
    templateUrl: './widget.component.html',
    styleUrls: ['./widget.component.scss']
})
export class WidgetComponent implements OnInit {
    title = '';

    constructor() {
    }

    ngOnInit() {
        this.title = 'widget - static component';
    }
}

and create a custom configuration with:

"fileReplacements": [
                                {
                                    "replace": "src/environments/environment.ts",
                                    "with": "src/environments/environment.prod.ts"
                                },
                                {
                                    "replace": "src/app/widget/widget.component.ts",
                                    "with": "src/app/widget/widget-static/widget.component.ts"
                                }
]

But ng build -c static-demo has error:

```ERROR in : Cannot determine the module for class WidgetComponent in /..../src/app/widget/widget-static/widget.component.ts! Add WidgetComponent to the NgModule to fix it.


NgModule has path until original component **src/app/widget/widget.component.ts**.

![image](https://user-images.githubusercontent.com/2271337/46077162-3137ec00-c190-11e8-92a9-0dd8e7b5769c.png)

if I remove html+styles, because I don't need, then `ng build` has another error:

ERROR in : Couldn't resolve resource ./widget.component.scss relative to /..../src/app/widget/widget-static/widget.component.ts
```

do I something wrong?

@shprink

We also needed different variable configurations for different customers.

This is the workaround we used:

src/
    scss/
        product_configurations/
            classic/
                _product_configuration.scss
                _fonts.scss
                ...
            special-customer/
                _product_configuration.scss
                _fonts.scss
                ...
            ...
        _variables.scss
        style.scss

src/scss/_variables.scss

@import '_product_configuration';

/** Other variables, e.g. using colour functions, etc. */
/** [...] */

src/scss/style.scss

@import '_variables';
@import '_fonts';

/** Other imports, that use the variables, e.g. Bootstrap, Angular Material */
/** [...] */

/** Other styles */
/** [...] */

Excerpt from angular.json:

          "configurations": {
            "classic": {
              "stylePreprocessorOptions": {
                "includePaths": ["src/scss/product_configurations/classic"]
              },
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.classic.ts"
                },
                {
                  "replace": "src/index.html",
                  "with": "src/index.classic.html"
                }
              ]
            },
            "special-customer": {
              "stylePreprocessorOptions": {
                "includePaths": ["src/scss/product_configurations/special-customer"]
              },
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.special-customer.ts"
                },
                {
                  "replace": "src/index.html",
                  "with": "src/index.special-customer.html"
                }
              ]
            }
          }

@richardtreier thanks for sharing that, it is a working workaround !

Using angular 7.2 I'm able to replace html files but not for scss files. Is there any updates?

Using angular 7.2 I'm able to replace html files but not for scss files. Is there any updates?

Actually html files don't work for me with 7.3

I am also unable to replace html files with 7.1, using code in my angular.json which previously worked:

"fileReplacements": [
  {
    "replace": "src/environments/environment.ts",
    "with": "src/environments/environment.prod.ts"
  },
  {
    "replace": "src/index.html",
    "with": "src/index.prod.html"
  }
]

Results in a build that uses index.html, and does not use index.prod.html

UPDATE: After trying to update packages in various manners, oddly enough, literally doing mv index.prod.html index.prod.html and then yarn build again fixed it, and the changes were picked up and built properly. Very odd.

any news on this? Is this going to be implemented?

@shprink did your solution work with angular 7.x? It didn't work for me ...

Same problem in here with Angular 7.2.10 (2019-03-20)

I'm using this workaround meanwhile: https://github.com/angular/angular-cli/issues/11451#issuecomment-424715627

@jesussobrino I use a major class in my body and use the environment to exchange it in the view. This causes a lot more css code but works pretty well. This also allows changeable themes as a nice sideeffect.

import { Component } from '@angular/core';
import {environment} from '../environments/environment';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  constructor() {
    document.body.classList.add(environment.theme);
  }
}

@richardtreier 's workaround didn't work for me (Angular 7.2) but I managed to achieve similar results (I guess) by redefining the styles entrypoint for each configuration.

in angular.json:

"configurations": {
     "my-theme": {
           "styles": ["src/product_configurations/my-theme/styles.scss"]
     },
}

Then for instance inproduct_configurations/my-theme/styles.scss:

@import "../../styles";     // Still get the common styles first

body.my-theme {             // my-theme-specific styling
    @import "components";   // my-theme-specific components styling under product_configurations/my-theme
}

@Javarome This is how I did it too. Thinking more about the topic, I came to the conclusion that working with css variables is the best you can do. There is a polyfill for IE9+. Using var() will save you a lot of css code in the final build.

@konsultaner Yes this should be better for colors and other variables, but how can this help providing different CSS code per theme, beyond properties' values?

@Javarome Thats definitly not working with variables.. Thats right.

I also need this functionality. We have a white-labelled application that need specific .pdf, .scss, .png etc. files to be copied. In the case of .scss, these need to be replaced at pre-build.

I want to avoid having multiple projects/configurations/apps defined in the angular.json, because adding new brands would require a change to the base code each time.
If .scss files worked with "fileReplacements", we could overcome this, by keeping the "branded" differences in different repositories and simply take the files from those during build.

In our case, TeamCity and OctoDeploy would take care of the rest.

I need this feature for the ability to build the app using a specific theme which I can pass using a parameter.

Hi, I need this functionality for different theme of my application.

Thank you @richardtreier for this very nice solution. Just implemented with angular-cli 7.3.9 and it works as advertised. My initial difficulties were due to my own errors; perhaps others who claim this doesn't work may wish to revisit.

Here is my solution to this problem
I wrote npm scripts in package.json file. Which copy the necessary files before starting ng build. I have two apps app1 and app2 with different themes. The production build command are
app1
npm run build:app1:prod

app2
npm run build:app2:prod

"scripts": {
    "ng": "ng",
    "serve:app1": "npm run skin:app1 && ng serve app1 -c dev -o",
    "serve:app2": "npm run skin:app2 && ng serve app2 -c dev -o",
    "build:app1:stage": "npm run skin:app1 && ng build app1 -c stage --prod",
    "build:app1:prod": "npm run skin:app1 && ng build app1 --prod",
    "build:app2:stage": "npm run skin:app2 && ng build app2 -c stage --prod",
    "build:app2:prod": "npm run skin:app2 && ng build app2 --prod",
    "skin:app1": "cp -arv src/apps/app1/skin/. node_modules/ng-uikit-pro-standard/assets/scss/",
    "skin:app2": "cp -arv src/apps/app2/skin/. node_modules/ng-uikit-pro-standard/assets/scss/"
  }

Thanks @richardtreier your solution worked perfectly!

Since I'm a bit new to Angular CLI I had a bit of trouble understanding what stylePreprocessorOptions does. For anyone needing a bit more information about stylePreprocessorOptions you can have a look at this article:

https://scotch.io/tutorials/angular-shortcut-to-importing-styles-files-in-components

I am trying to replace with robots.txt , doesn't work as well .

@karocksjoelee robots.txt should work.
This issue is related only to css files (where sass processor is used).
All other types of file replacements work.

In angular.json:
Make sure you first add robots.txt to build.assets so it gets copied at build time

"assets": [
              { "glob": "**/*", "input": "src/assets/", "ignore": ["**/*.ts"], "output": "/assets/" },
              { "glob": "favicon.ico", "input": "src/", "output": "/" },
              { "glob": "robots.txt", "input": "src/", "output": "/" }
            ],

and then apply the file replacements:

"fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                },
                {
                  "replace": "src/robots.txt",
                  "with": "src/robots.prod.txt"
                }
              ],

Still doesn't work in 2020 !

The functionality of fileReplacements block is not very sophisticated because it just replaces files. The main problem is that it doesn't affect the resolve process of imports, thus breaks for almost all files that have transitive relative imports.

For example if you have the following structure

  • app

    • components

    • test



      • test.component.scss


      • test.component.html


      • test.component.ts



  • replacements

    • test-new.component.ts (with templateUrl and styleUrl "../app/components/test/test.component.xxx")

and you want to replace the test.component.ts with test-new.component.ts, it won't work because the file content just replaces the content of the original test.component.ts in the test folder. The build process then looks up the imports like templateUrl and styleUrls which cannot be resolved in that context.

There may be a way to have a working folder structure to deal with such problems, but it's very cumbersome.

Because we need such functionality I'm using webpack with a custom webpack-resolve plugin instead of angular-cli. For the moment it works fine when using JIT and a bit limited when using AOT. But with angular 9 and ivy, new issues arrived. The hassle is real.

There really should be a more sophisticated "fileReplacement" option that deals with the correct resolvement and imports of replaced files.

json files still does not work

"assets": [
  "src/config"
],
"fileReplacements": [
  {
    "replace": "src/environments/environment.ts",
    "with": "src/environments/environment.int.ts"
  },
  {
    "replace": "src/config/config.json",
    "with": "src/config/config.int.json"
  }
]

@manoyanx
as a workaround you can import the json files in your environment

@tmtron, actually, fileReplacements is not meant to replace files which are outside of bundles. See https://github.com/angular/angular-cli/issues/16779#issuecomment-579183693

@tmtron, actually, fileReplacements is not meant to replace files which are outside of bundles. See #16779 (comment)

I agree, but in this case the json files src/config/config*.json are in the bundle, right?
i.e. the json-file can be imported in typescript code, which is transpiled into js-code and thus part of an application bundle.
or the other way around: these files are not assets: i.e. you don't want to load them from your client application on demand.

The assets don't count as part of the JS bundles, and he's been using that, see:

"assets": [
  "src/config"
  "..."
],

They are part of the bundle only if you import them directly using the --resolveJsonModule that you've linked above. Basically, if use use that flag with this kind of import, then you can use fileReplacemens:

import * as APP_CONFIG  from './app.config.json';

Otherwise, you'll need something like this: https://github.com/angular/angular-cli/issues/7506#issuecomment-324981817

More related info: #3855, #7506, #7704


P.S. @manoyanx, your comment and the discussion that followed is not related to the original issue, if the above linked issues don't help, you should create a new issue.

@SchnWalter oh right - I missed the assets config and only looked at the path, thanks!

Its a very important feature, please do give a fix on this ASAP.

On working with Internationalization it is very much essential thing. Also the component specific files should also be get replaced by this Angular config.

I am i18n for Internationalization, but the issue is with the SCSS file replacement for Arabic layout.

Hi asphub, for the Internationalization you can use the i18n built in mechanism provided by Angular !

@mcescalante It's been awhile since your post but for index files try using this instead of "fileReplacements":

"index": {
  "input": "src/index.prod.html",
  "output": "index.html"
 },

This also will be useful and easier for robots.txt replacement for production.

@filipesilva @alan-agius4 any update on this?

Hi asphub, for the Internationalization you can use the i18n built in mechanism provided by Angular !

@kodze I am already using i18n for Internationalization, but the issue is with the SCSS file replacement for the UI to switch Arabic layout.

I am using SCSS variables to manage this

Was this page helpful?
0 / 5 - 0 ratings

Related issues

IngvarKofoed picture IngvarKofoed  路  3Comments

gotschmarcel picture gotschmarcel  路  3Comments

ericel picture ericel  路  3Comments

brtnshrdr picture brtnshrdr  路  3Comments

jmurphzyo picture jmurphzyo  路  3Comments