Skip to content

fileReplacements doesn't work with css files #11451

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Lionardo opened this issue Jul 4, 2018 · 45 comments · Fixed by #19310
Closed

fileReplacements doesn't work with css files #11451

Lionardo opened this issue Jul 4, 2018 · 45 comments · Fixed by #19310

Comments

@Lionardo
Copy link

Lionardo commented Jul 4, 2018

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

@Serrulien
Copy link

Serrulien commented Jul 6, 2018

As said in #10881 (comment), 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 #10881 (comment).

@filipesilva
Copy link
Contributor

We added the generic replacement functionality in angular/devkit#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.

@wctiger
Copy link

wctiger commented Jul 27, 2018

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
}

@Lionardo
Copy link
Author

Lionardo commented Aug 7, 2018

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?

@richardtreier
Copy link

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

OR

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

Replacing scss files still not working.

@developcodeza-matthew
Copy link

developcodeza-matthew commented Sep 5, 2018

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

@tobiaseisenschenk
Copy link

tobiaseisenschenk commented Sep 11, 2018

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.

@shprink
Copy link

shprink commented Sep 26, 2018

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

@developcodeza-matthew
Copy link

@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

@WuglyakBolgoink
Copy link

WuglyakBolgoink commented Sep 26, 2018

@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:

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

image

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?

@richardtreier
Copy link

richardtreier commented Sep 26, 2018

@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"
                }
              ]
            }
          }

@shprink
Copy link

shprink commented Oct 5, 2018

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

@Subhendu1990
Copy link

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

@ngbot ngbot bot added this to the Backlog milestone Jan 24, 2019
@kojilab
Copy link

kojilab commented Feb 22, 2019

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

@mcescalante
Copy link

mcescalante commented Mar 18, 2019

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.

@konsultaner
Copy link

konsultaner commented Mar 19, 2019

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 ...

@jesussobrino
Copy link

jesussobrino commented Mar 27, 2019

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

I'm using this workaround meanwhile: #11451 (comment)

@konsultaner
Copy link

@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);
  }
}

@Javarome
Copy link

Javarome commented May 2, 2019

@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
}

@konsultaner
Copy link

@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.

@Javarome
Copy link

Javarome commented May 2, 2019

@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?

@konsultaner
Copy link

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

@mortware
Copy link

mortware commented Jun 17, 2019

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.

@takahser
Copy link

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

@rssaranece94
Copy link

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

@jgeiger-cr
Copy link

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.

@kalimulhaq
Copy link

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/"
  }

@dangrima90
Copy link

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

@karocksjoelee
Copy link

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

@danmana
Copy link

danmana commented Nov 11, 2019

@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"
                }
              ],

@kodze
Copy link

kodze commented Feb 5, 2020

Still doesn't work in 2020 !

@chumbalum
Copy link

chumbalum commented Feb 18, 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.

@manoyanx
Copy link

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"
  }
]

@tmtron
Copy link

tmtron commented May 29, 2020

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

@SchnWalter
Copy link
Contributor

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

@tmtron
Copy link

tmtron commented May 29, 2020

@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.

@SchnWalter
Copy link
Contributor

SchnWalter commented May 29, 2020

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: #7506 (comment)

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.

@tmtron
Copy link

tmtron commented May 29, 2020

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

@asphub
Copy link

asphub commented Jun 7, 2020

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.

@kodze
Copy link

kodze commented Jun 8, 2020

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

@maxtacco
Copy link

maxtacco commented Jun 9, 2020

@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"
 },

@BruneXX
Copy link

BruneXX commented Aug 7, 2020

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

@xiaoxiangmoe
Copy link

@filipesilva @alan-agius4 any update on this?

@asphub
Copy link

asphub commented Oct 5, 2020

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

alan-agius4 added a commit that referenced this issue Nov 6, 2020
… values

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.

Closes #11451
alan-agius4 added a commit that referenced this issue Nov 6, 2020
… values

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.

Closes #11451

(cherry picked from commit 424af28)
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Dec 7, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.