postcss-sprites icon indicating copy to clipboard operation
postcss-sprites copied to clipboard

Webpack doesn't combine all images into single sprite

Open MwumLi opened this issue 7 years ago • 18 comments

I use code to express my mean:

// aa.css
.a {
  background: url(./img/sprites/default/table/edit-icon.png)
}

// bb.css
.b {
  background: url(./img/sprites/default/table/offline-icon.png)
}
// main.js
require('aa.css');
require('bb.css'))

when I use webpack, generated a sprite : sprite.table.png, but only offline-icon was bundle into sprite.table.png, the correct result should be sprite.table.png include edit-icon.png and offline-icon.png both. when use require and run loader once and you cann't make cache for this, so the 'wrong' result appear

can you solve the question ?

MwumLi avatar Oct 03 '16 07:10 MwumLi

@MwumLi Can you give more information about your setup of Webpack?

vvasilev- avatar Oct 04 '16 05:10 vvasilev-

webpack config :

module.exports = {
    entry: {
        main: './src/main.js'
    },
    output: {
        path: './dist/',
        filename: 'js/[name].js',
        publicPath: '/'
    },
    module: {
        loaders: [
            {
                test: /\.css$/,
                loader: "style!css!postcss"
            },
            {
                test: /\.(png|jpg)$/,
                loader: 'ul-loader',
                query: {
                    limit: 1,
                    name: "img/[name].[hash].[ext]"
                }
            }
        ]
    }.
    plugins: [
        new HtmlWebpackPlugin({
            title: "webpack demos",
            filename: "index.html"
        })
    ],
   postcss: function() {
        return [postcssSprites({
          stylesheetPath: "./dist/css/",
          spritePath: "./dist/img/",
          relativeTo: 'file',
          basePath: './src/',
          filterBy: function(image) {
            if (image.url.indexOf('img/sprites') === -1) {
              return Promise.reject();
            }
            return Promise.resolve();
          },
          groupBy: function(image) {
            if (image.url.indexOf('img/sprites') === -1) {
              return Promise.resolve(Date.now().toString());
            } else {
              return Promise.resolve(image.url.split('/').slice(-2, -1)[0])
            }
          },
          hooks: {
            onUpdateRule: function(rule, comment, image) {
                var spriteUrl = image.spriteUrl;
                image.spriteUrl = dir.publicPath + dir.dist.img + spriteUrl.split('/').pop();
                postcssSpritesCore.updateRule(rule, comment, image);
            }
        /*                  ,*/
            //onSaveSpritesheet: function(opts, groups) {
                 //return path.join(opts.spritePath, ['sprite', ...groups, _.now(), 'png'].join('.'));
            /*}*/
          },
          spritesmith: {
            padding: 8
          }
        })]
    }
}

MwumLi avatar Oct 08 '16 01:10 MwumLi

I'm having the same issue with a pretty simple webpack config:

const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

const isPROD = process.env.NODE_ENV === 'production';

/**
 * Plugins
 */
const plugins = [
  new ExtractTextPlugin('common.css', {allChunks: true})
];


const styleLoaders = ['css-loader', 'postcss'];
const sprites = require('postcss-sprites');

/**
 * Paths
 */
const SRC = path.resolve('./src');
const EXT = path.resolve('./node_modules');
const OUT = path.resolve('./dist/bundle');

module.exports = {
  devtool: isPROD ? 'source-map' : 'eval',
  entry: {
    common: ['./src/index']
  },
  output: {
    filename: '[name].js',
    chunkFilename: 'chunk.[id].js',
    path: OUT,
    /* prod setting is for CSS images; js path is provided at runtime */
    publicPath: isPROD ? '' : 'http://localhost:8888/static/'
  },
  plugins,
  module: {
    loaders: [{
      test: /\.css$/,
      loader: isPROD ? ExtractTextPlugin.extract(styleLoaders) : ['style'].concat(styleLoaders).join('!'),
      include: [SRC, EXT]
    }, {
      test: /\.(gif|jpg|png)$/,
      loader: `file-loader?name=${isPROD ? '../' : ''}assets/[hash].[ext]`
    }]
  },
  resolve: {
    modulesDirectories: [
      'src',
      'node_modules'
    ],
    extensions: ['', '.js', '.json', '.jsx', '.gif', '.png', '.jpg']
  },
  postcss: [
    sprites({
      stylesheetPath: SRC,
      spritePath: 'sprites',
      basePath: SRC
    })
  ]
};

The last stylesheet to get processed overwrites the sprite. Does this only work as a secondary process after the full stylesheet has been generated?

evandavis avatar Nov 14 '16 17:11 evandavis

This is the same issue as #43; Webpack processes files individually, so the sprite contains only the last file.

evandavis avatar Nov 14 '16 21:11 evandavis

same issue here.

lcxfs1991 avatar Nov 29 '16 02:11 lcxfs1991

same to me! how to fix this?

skyujilong avatar Dec 05 '16 10:12 skyujilong

Hi guys,

The problem is that your files are processed individually by Webpack while the plugin expects a single CSS stylesheet. I'm open for discussion if someone have an idea how this can be fixed.

vvasilev- avatar Dec 07 '16 08:12 vvasilev-

I have the same issue with vue...

{
    test: /\.vue$/,
    loader: 'vue-loader',
    options: {
        postcss: [cssnext(), sprites(spritesOptions), px2rem()],
        loaders: {
            css: ExtractTextPlugin.extract({
            loader: 'css-loader',
            fallbackLoader: 'vue-style-loader'
            })
        }
     }
}

gaaoge avatar Dec 21 '16 03:12 gaaoge

I have the same issue, Just in css.

// a.css
.box1 {
  background: url(./sprite/icon1.png);
}
.box2 {
  background: url(./sprite/icon2.png)
}
.box3 {
  background: url(./sprite/icon3.png)
}

// b.css
.box1 {
  background: url(./sprite/icon1.png);
}
.box2 {
  background: url(./sprite/icon2.png)
}

Then I use gulp to get sprite img.

gulp.task('dist', function (done) {
    var opts = {
        stylesheetPath: './dist',
        spritePath: './img',
        spritesmith: {padding: 4},
        retina:  2,
        hooks: false,
        groupBy: function (image) {
            var groupName = 'x';
            image.retina = true;
            image.ratio = 2;
            return Promise.resolve(groupName);
        }
    };

    var stream = gulp.src('./css/*.css')
        .pipe(postcss([sprites(opts)]))
        .pipe(gulp.dest('./dist'));
    return stream;
});

I got two new css. And They use the same sprite img. But the same sprite img only has icon1.png and icon2.png. There ditn't has icon3.png in the sprite img.

perfey avatar Jan 12 '17 04:01 perfey

I use the following webpack CSS rule:

exports.css = {
    test: /\.css$/,
    use: [
        'style-loader',
        'css-loader',
        {
            loader: 'postcss-loader',
            options: { plugins: postcssInit }
        }
    ]
};

function postcssInit() {
    const sheetName = basename(this.resourcePath, '.css');
    return loadPlugins.call(this, { sheetName });
}

function loadPlugins({ sheetName }) {
    return [
        // some plugins...
        require('postcss-sprites')({
            groupBy: () => Promise.resolve(sheetName),
            // more opts...
        }),
        // more plugins...
    ];
}

See https://github.com/2createStudio/postcss-sprites/issues/83 for some more details about my setup as well as https://github.com/2createStudio/postcss-sprites/pull/85.

jednano avatar Feb 24 '17 23:02 jednano

Does that mean postcss-sprite will not work with webpack?

hoanguyen311 avatar Mar 17 '17 12:03 hoanguyen311

same issue ...

hycript avatar Mar 30 '17 10:03 hycript

same issue+1

inoutw avatar Jul 10 '17 07:07 inoutw

same issue+1

shenzhim avatar Aug 15 '17 04:08 shenzhim

Has the problem been solved?

SilenceOfNight avatar Dec 28 '17 01:12 SilenceOfNight

same issue+1

xierenyuan avatar Aug 14 '18 11:08 xierenyuan

same issue+1

yeungtg avatar Aug 14 '18 11:08 yeungtg

same issue+1

Chorer avatar Aug 18 '21 02:08 Chorer