Search code examples
angularwebpackangular-cliwebpack-4angular-builder

Angular inline resources in css as data-uri


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.


Solution

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