Skip to content
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

"module export" instead of svg directly #524

Closed
urbanbratcoder opened this issue Aug 2, 2021 · 6 comments
Closed

"module export" instead of svg directly #524

urbanbratcoder opened this issue Aug 2, 2021 · 6 comments

Comments

@urbanbratcoder
Copy link

urbanbratcoder commented Aug 2, 2021

Hi Folks,
in css there will be generated
background-image: url(data:image/svg+xml;base64,bW9kdWxlLmV4cG9ydHMgPSAiZGF0YTpp...
the base64-encoded part is
module.exports = "data:image/svg+xml,%3csvg viewBox='0 0 32.72 90.57' xmlns='http://www.w3.org/2000/svg'%3e%3cg fill='none'...

My webpack config:

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  entry:
  {
    "sitea": "./src/sitea",
    "siteb "./src/siteb",
    "sitec "./src/sitec",
  },
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].dev.js',
    publicPath: '/'
  },
  module: {
    rules: [
          {
        test: require.resolve("jquery"),
        loader: "expose-loader",
        options: {
          exposes: ["$", "jQuery"],
        },
      },
      {
        test: [/.js$|.ts$/],
        exclude: /(node_modules)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              '@babel/preset-env'
            ]
          }
        }
      },
      {
        test: [/.css$|.scss$/],
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'sass-loader',
          'postcss-loader'
        ]
      },
      {
        test: /\.svg/,
        type: 'asset/inline',
        use: {
          loader: 'svg-url-loader',
        }
      }
    ]
  },
  resolve: {
    alias: {
      '@scss': path.resolve(__dirname, '../src/scss'),
      '@js': path.resolve(__dirname, '../src/js'),
      '@': path.resolve(__dirname, '../src')
    },
    modules: [
      'node_modules',
      path.resolve(__dirname, '../src')
    ],
    extensions: ['.js'],
  },
  plugins: [new MiniCssExtractPlugin({filename:'[name].dev.css'})]
}

I cannot find the cause of this. There is no other svg-loader.
My packages:

@babel/core@7.14.8
@babel/preset-env@7.14.9
autoprefixer@10.3.1
babel-loader@8.2.2
bootstrap-v4-rtl@4.6.0-2
cross-env@7.0.3
css-loader@6.2.0
css-minimizer-webpack-plugin@3.0.2
dayjs@1.10.6
expose-loader@3.0.0
jquery@3.6.0
lazysizes@5.3.2
lity@2.4.1
mini-css-extract-plugin@2.1.0
mmenu-js@8.5.24
moment@2.29.1
popper.js@1.16.1
postcss-loader@6.1.1
postcss@8.3.6
sass-loader@12.1.0
sass@1.37.0
svg-url-loader@7.1.1
swiper@6.8.0
webpack-cli@4.7.2
webpack@5.47.1

Does anybody got a hint for me?

@bhovhannes
Copy link
Owner

Hi, are you using webpack 5 ?

I am asking because we have a PR #478 which has test failures, which makes me think that something in the code is not compatible with webpack 5.

@urbanbratcoder
Copy link
Author

Yes, i do. If webpack 5 is the problem, then that's curious. Because it worked with webpack 5 up until recently. I try to find out what changed.

@urbanbratcoder
Copy link
Author

I got good and bad news. Svg-url-loader was responsible for "model export" within the data uri. The "type: 'asset/inline'" triggers the builtin asset module from webpack 5, making the conversion happening twice. Removing svg-url-loader resulting in

{
  test: /\.svg/,
  type: 'asset/inline'
}

gets it working again. Unfortunately i have not been able to get it to work that svg-url-loader is solely responsible. It is a pity and i hope that svg-url-loader (imho the more comfortable solutioni) will be available again under webpack 5. Unfortunately my js knowledge is not enough to support the project more deeply here.

@LambdaZed
Copy link

LambdaZed commented Sep 7, 2021

I got good and bad news.

You can use 'asset/inline' with generator property

{
  test: /\.svg$/,
  type: 'asset/inline',
  generator: {
    dataUrl(content) {
      const REGEX_XML_DECLARATION = /^\s*<\?xml [^>]*>\s*/i;
      const REGEX_MULTIPLE_SPACES = /\s+/g;
      const REGEX_DOUBLE_QUOTE = /"/g;
      const REGEX_UNSAFE_CHARS = /[{}\|\\\^~\[\]`"<>#%]/g;

      const transformedSvgSource = content
        .toString('utf8')
        .replace(REGEX_XML_DECLARATION, '')
        .replace(REGEX_MULTIPLE_SPACES, ' ')
        .replace(REGEX_DOUBLE_QUOTE, "'")
        .replace(REGEX_UNSAFE_CHARS, uriEncodeMatch)
        .trim();

      const data = 'data:image/svg+xml,' + transformedSvgSource;

      function uriEncodeMatch(match) {
        return '%' + match[0].charCodeAt(0).toString(16).toLowerCase();
      }

      return data;
    },
  },
}

Or you can simply use mini-svg-data-uri

const miniSVGDataURI = require('mini-svg-data-uri');
...
{
  test: /\.svg$/,
  type: 'asset/inline',
  generator: {
    dataUrl: (content) => miniSVGDataURI(content.toString()),
  },
}

@lada
Copy link

lada commented Sep 14, 2021

From Webpack 5 documentation:

When using the old assets loaders (i.e. file-loader/url-loader/raw-loader) along with Asset Module in webpack 5, you might want to stop Asset Module from processing your assets again as that would result in asset duplication. This can be done by setting asset's module type to 'javascript/auto'.

{
  test: /\.svg(\?.*)?$/,
  use: [
    'svg-url-loader',
  ],
  type: 'javascript/auto',
},

Works perfect for me.

@urbanbratcoder
Copy link
Author

Thank you guys! LambdaZed's solution got me learning something again and lada's hint for the documentation is absolutely right. I somehow read over this paragraph.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants