3

Is there any way in Angular app without ejecting webpack configuration to have inlined resources, as SVGs in CSS background properties?

I have already tried custom webpack builder to remove built-in file-loader rule and add uri-loader, but, SVGs are not inlined.

I suspect that it may be somehow related to internal SCSS/CSS processing using postcss.

glinda93
  • 7,659
  • 5
  • 40
  • 78
Zygimantas
  • 8,547
  • 7
  • 42
  • 54
  • you could try ngStyle ... however most of the time you will need to run Sanitizer on the data: https://angular.io/api/platform-browser/DomSanitizer here is another question that should be similar: https://stackoverflow.com/questions/45991613/angular-how-do-i-sanitize-a-style-inside-a-plain-typescript-class – Tim.Burnell Aug 08 '20 at 15:49

1 Answers1

5

As you said, url-loader does not handle urls in style files. To do that, you need to use postcss-url package.

To use a custom webpack configuration without ejecting, you need to install this package. After configuring your workspace with the directions provided in that package, you can use the following webpack configuration:

const url = require('postcss-url');    
const urlPlugin = url([{ filter: '**/*.svg', url: 'inline' }]);
    
module.exports = config => {
  for (const rule of config.module.rules) {
      const loader = rule.use && rule.use.find(x => x.loader === 'postcss-loader');
      if (loader) {
          loader.options.plugins = [urlPlugin];
      }
  }
    
  return config;
};

Basically, this code adds postcss-url plugin to every postcss-loader in Angular's default webpack configuration.

You can edit this configuration to customize for your needs. For example, you can use maxSize parameter to exclude files greater than a certain size. Read postcss-url repo for all options.

EDIT by Zygimantas:

I am accepting your answer as correct and adding a Typescript version of webpack.config.ts

import { Configuration, NewLoader } from 'webpack'
import * as PostCssUrlPlugin from 'postcss-url'

export default (config: Configuration) => {
    if (config.module === undefined) {
        throw new Error()
    }

    let patched = false
    for (const rule of config.module.rules) {
        if ('use' in rule && Array.isArray(rule.use)) {
            const loader = rule.use.find(
                (a): a is NewLoader =>
                    typeof a !== 'string' &&
                    'options' in a &&
                    a.loader !== undefined &&
                    a.loader.includes('postcss-loader'),
            )

            if (loader !== undefined && loader.options !== undefined) {
                loader.options.plugins = [PostCssUrlPlugin([{
                  filter: '**/*.svg',
                  url: 'inline',
                }])]

                patched = true
            }
        }
    }

    if (!patched) {
        throw new Error('Could not patch webpack configuration')
    }

    return config
}

NOTE:

In Javascript version, I had to use x.loader.includes('postcss-loader') instead of x.loader === 'postcss-loader' because loader value was a full path in my case.

Zygimantas
  • 8,547
  • 7
  • 42
  • 54
Gokhan Kurt
  • 8,239
  • 1
  • 27
  • 51
  • I will be able to award a bounty in 17 hours since now. – Zygimantas Aug 08 '20 at 22:10
  • 1
    @Zygimantas thanks for the edit. By the way, Angular may change their webpack configuration from version to version, so watch out for that. My original answer was for Angular 8. – Gokhan Kurt Aug 08 '20 at 22:27