cascornelissen / svg-spritemap-webpack-plugin

SVG spritemap plugin for webpack
MIT License
207 stars 50 forks source link

SASS mixin sprites won't resize. #133

Closed pdrittenhouse closed 3 years ago

pdrittenhouse commented 3 years ago

I'm using the SASS mixin to add sprites as background images and everything seems to work fine (the icons show up and the color works) except I'm not able to resize the images. I'm assuming that the svg's aren't being minified so the whitespace isn't being removed. I have the svgo option set to "true" and even have svgo installed globally. I'm not sure that it's actually processing the svg's before emitting the data uri's in the scss. The plugin version I'm using is "^3.7.1" and the webpack version is "^5.4.0".

Here's my webpack config:

const ESLintPlugin = require('eslint-webpack-plugin');
const StylelintPlugin = require('stylelint-webpack-plugin');
const postcssPresetEnv = require('postcss-preset-env');
const cssnano = require('cssnano');
const autoprefixer = require('autoprefixer');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const PostCSSAssetsPlugin = require('postcss-assets-webpack-plugin');
const mqpacker = require('mqpacker');
const sassExportData = require('@theme-tools/sass-export-data');
const SVGSpritemapPlugin = require('svg-spritemap-webpack-plugin');
const { ProgressPlugin, ProvidePlugin } = require('webpack');
const paths = require('./paths');

module.exports = {
  entry: {
    public: [`${paths.src}/index.js`],
  },
  output: {
    path: paths.build,
    filename: 'js/[name].bundle.js',
    publicPath: '/wp-content/themes/public/dist/wp/',
  },
  module: {
    rules: [
      // JavaScript
      {
        test: /\.js$/,
        exclude: [
          /(node_modules|vendor|wp-admin|wp-includes|plugins|twentyfifteen|twentysixteen|twentyseventeen|twentynineteen|libs|bundle|dist)/,
        ],
        use: {
          loader: 'babel-loader',
          options: {
            babelrc: false,
            presets: [
              ['@babel/preset-env', { modules: false }],
              '@babel/preset-react',
            ],
          },
        },
      },
      // Images
      {
        test: /\.(?:ico|gif|png|jpg|jpeg|svg)$/i,
        exclude: [/(fonts|svg\/svg)/, '/node_modules/@fortawesome/'],
        type: 'asset/resource',
        generator: {
          filename: 'img/[name][ext][query]',
        },
      },
      // Fonts
      {
        test: /\.(woff(2)?|eot|ttf|otf|svg?)(\?[a-z0-9]+)?$/,
        exclude: [/(img|svg\/svg)/],
        type: 'asset/resource',
        generator: {
          filename: 'fonts/[name][ext][query]',
        },
      },
      // CSS, PostCSS, and Sass
      {
        test: /\.(scss|css)$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: { sourceMap: true },
          },
          {
            loader: 'postcss-loader',
            options: {
              sourceMap: true,
              postcssOptions: {
                ident: 'postcss',
                plugins: [
                  postcssPresetEnv(),
                  autoprefixer({ overrideBrowserslist: 'last 2 version' }),
                  cssnano(),
                ],
              },
            },
          },
          {
            loader: 'resolve-url-loader',
            options: {
              sourceMap: true,
              keepQuery: true,
            },
          },
          {
            loader: 'sass-loader',
            options: {
              sourceMap: true,
              sassOptions: {
                // Used to generate JSON about variables like colors, fonts
                // Used for patternlab demo data
                functions: sassExportData({
                  name: 'export_data',
                  path: `${paths.plsrc}/_data/`,
                }),
                // Enable Sass to import other components via, eg:
                // `@import 01-atoms/thing/thing`
                includePaths: `${paths.plsrc}/_patterns/`,
              },
              // ALL Sass partials should be provided with non-printing
              // variables, mixins, and functions
              additionalData: '@import "00-protons/variables";',
            },
          },
        ],
      },
    ],
  },
  plugins: [
    // Show build progress
    new ProgressPlugin({ profile: false }),
    // Provide "global" vars mapped to an actual dependency.
    // Allows e.g. jQuery plugins to assume that `window.jquery` is available
    new ProvidePlugin({
      // Bootstrap is dependant on jQuery and Popper
      $: 'jquery',
      jQuery: 'jquery',
      'window.jQuery': 'jquery',
      Popper: ['popper.js', 'default'],
    }),
    // Lint Javascript
    new ESLintPlugin(),
    // Lint Styles
    new StylelintPlugin(),
    // Extract CSS into separate files
    new MiniCssExtractPlugin({
      filename: 'css/[name].css',
      chunkFilename: '[id].css',
    }),
    // Manage postcss assets
    // @link https://css-tricks.com/images-in-postcss/
    new PostCSSAssetsPlugin({
      test: /\.css$/,
      log: true,
      plugins: [
        // Pack same CSS media query rules into one media query rule
        mqpacker,
      ],
    }),
    // Generate SVG Spritemap
    new SVGSpritemapPlugin(`${paths.svg}/svg/*.svg`, {
      styles: {
        filename: `${paths.svg}/generated/_icons-generated.scss`,
        variables: {
          sizes: 'svgicon-sizes', // Prevent collision with Bootstrap $sizes
          variables: 'svgicon-variables',
        },
      },
      output: {
        filename: 'spritemap.svg',
        svg4everybody: true,
        svgo: true,
      },
    }),
  ],
};

Here's an example of an svg I'm using:

`

`

And here's a codepen example of what's generated by the plugin vs the same svg minified or base64 encoded. Is there a way to ensure that the svg is optimized or base64 encoded before the data uri is generated? Maybe there's a setting or something I'm missing?

cascornelissen commented 3 years ago

This is caused by the fact that the width and height attributes on the svg element appear to be missing from the generated SVG.

Changing this line in your Codepen:

background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3cpath fill='%23ff000' width='100%' height='100%' d='M27.987 4.122L17.849 14.126l8.73 8.616-4.046 3.993-8.731-8.616-8.985 8.868-4.164-4.109 8.986-8.867-9.626-9.5L4.059.517l9.627 9.5L23.823.013l4.164 4.109z'/%3e%3c/svg%3e") 50% no-repeat;

To the following shows the correct SVG:

background: url("data:image/svg+xml,%3csvg width='28' height='27' xmlns='http://www.w3.org/2000/svg'%3e%3cpath fill='%23ff000' width='100%' height='100%' d='M27.987 4.122L17.849 14.126l8.73 8.616-4.046 3.993-8.731-8.616-8.985 8.868-4.164-4.109 8.986-8.867-9.626-9.5L4.059.517l9.627 9.5L23.823.013l4.164 4.109z'/%3e%3c/svg%3e") 50% no-repeat;

Usually this is caused by SVGO, could you try passing false to output.svgo? This could be caused by the removeDimensions plugin for example. It could also be that it's stripped somewhere else in the plugin, if disabling output.svgo doesn't solve the issue could you create a minimal repository that I can check out and run npm start to debug the issue?

pdrittenhouse commented 3 years ago

I tried disabling svgo and also tried passing in an options object to explicitly disable removeDimensions and neither worked. I've set up a repo here that reproduces the issue.

aschanMarcus commented 3 years ago

@cascornelissen it looks like height and width attributes are removed in multiple places.

/lib/generate-svg.js#L160-L162 /lib/style-formatters/scss.js#L53-L55

Perhaps height and width could be added again in the style formatters?

cascornelissen commented 3 years ago

@pdrittenhouse, thanks for the repository (probably the best I've seen so far) ❤️ And @aschanMarcus thanks for linking to the required logic. If you zoom out just a few lines it's wrapped in an if statement to check for options.keepAttributes.

https://github.com/cascornelissen/svg-spritemap-webpack-plugin/blob/13e6a341b1f93cd3f112b130f1d8693d70b5f5a3/lib/style-formatters/scss.js#L51-L59

This styles.keepAttributes option was added in 3.6.0 to fix another issue but setting it to true in the example repository actually solves the issue you seem to be running into as well.

Could you verify this solves the problem on your end as well?

pdrittenhouse commented 3 years ago

@cascornelissen, awesome! That worked. Thank you so much.

heyflo commented 3 years ago

I'm glad I've found this issue, I was scratching my head with this and noticed on my fresh install I was using the 3.9.1 while on my last project it was the 3.5.7. Thanks for the fix, going to update this on my projects 👍