2createStudio / postcss-sprites

Generate sprites from stylesheets.
MIT License
413 stars 50 forks source link

Webpack doesn't combine all images into single sprite #69

Open MwumLi opened 7 years ago

MwumLi commented 7 years ago

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 ?

vvasilev- commented 7 years ago

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

MwumLi commented 7 years ago

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
          }
        })]
    }
}
evandavis commented 7 years ago

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 commented 7 years ago

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

lcxfs1991 commented 7 years ago

same issue here.

skyujilong commented 7 years ago

same to me! how to fix this?

vvasilev- commented 7 years ago

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.

gaaoge commented 7 years ago

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'
            })
        }
     }
}
perfey commented 7 years ago

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.

jednano commented 7 years ago

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.

hoanguyen311 commented 7 years ago

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

hycript commented 7 years ago

same issue ...

inoutw commented 7 years ago

same issue+1

shenzhim commented 7 years ago

same issue+1

SilenceOfNight commented 6 years ago

Has the problem been solved?

xierenyuan commented 6 years ago

same issue+1

yeungtg commented 6 years ago

same issue+1

Chorer commented 3 years ago

same issue+1