CupOfTea696 / laravel-mix-imagemin

Laravel Mix imagemin plugin
MIT License
19 stars 14 forks source link

Specify To Directory #1

Closed bradley-varol closed 5 years ago

bradley-varol commented 5 years ago

I'm calling mix.imagemin inside a loop and I'd like to be able to specify a directory to output the processed file to like so:

mix.imagemin(
        'picks/*.jpg',
        {
            to: `public/assets/images/picks/${type.folder}/`,
            context: 'resources/assets/images/',
        }
);

This isn't working - the files are processed but put in a top level folder called picks in public...

CupOfTea696 commented 5 years ago

The second parameter are of mix.imagemin() is passed as the options parameter for the copy-webpack-plugin. If you look at the documentation for that plugin, you'll see that the options parameter doesn't support the to key. You should be using the first parameter, which is passed to the patterns parameter of the copy-webpack-plugin.

The code below should work, but I haven't tested this.

mix.imagemin(
    {
        from: 'picks/*.jpg',
        to: `assets/images/picks/${type.folder}/`,
    },
    {
        context: 'resources/assets/images/',
    },
);
bradley-varol commented 5 years ago

Thanks for replying.

I have changed my arguments to the following:

mix.imagemin(
        {
            context: 'resources/assets/images/picks',
            from: '*.jpg',
            to: `assets/images/picks/${type.folder}/`,
        },
        {
            progressive: true,
            svgoPlugins: [{removeViewBox: false}],
            use: [pngquant],
        },
    );

which works but I'm confused as to why because the method in Imagemin.js has arguments like so:

register(patterns, copyOptions = {}, imageminOptions = {}) {}

Isn't the first arg meant to be for patterns? And what about the third option for imageminOptions?

Also, currently this is inside a loop like so:

const imageTypes = [
    {folder: 't'},
    {folder: 'sq'},
    {folder: 'sql'},
    {folder: 's'},
    {folder: 'm'},
    {folder: 'l'},
];
imageTypes.forEach((type) => {

    mix.imagemin(
        {
            context: 'resources/assets/images/picks',
            from: '*.jpg',
            to: `assets/images/picks/${type.folder}/`,
        },
        {
            progressive: true,
            svgoPlugins: [{removeViewBox: false}],
            use: [pngquant],
        },
    );

});

but only the last directory in the array ('l') is created. Any idea why?

CupOfTea696 commented 5 years ago

The first two arguments get passed directly to the two constructor arguments for the Copy Plugin, like so new CopyPlugin(patterns, copyOptions), while the third argument gets passed to the Imagemin Plugin, like so new ImageminPlugin(imageminOptions). Refer to the documentation of each of the plugins to see how exactly all of those can be used.

This also means that the code you've provided above is not passing the Imagemin options to the Imagemin Plugin. Additionally, you can also move the context option from patterns to copyOptions, given that it's shared for each pattern.

mix.imagemin(
    {
        from: '*.jpg',
        to: `assets/images/picks/${type.folder}/`,
    },
    {
        context: 'resources/assets/images/picks',
    },
    {
        progressive: true,
        svgoPlugins: [{removeViewBox: false}],
        use: [pngquant],
    },
);

As for why only the last directory is compiled, this is because the imagemin plugin gets re-registered every time in that loop and overwrites the previous call. This is a bug and I'll try to get that fixed asap.

However, you can prevent this by just passing all the patterns at once instead of calling mix.imagemin inside of a loop, like below.

const imageTypes = [
    {folder: 't'},
    {folder: 'sq'},
    {folder: 'sql'},
    {folder: 's'},
    {folder: 'm'},
    {folder: 'l'},
];
let patterns = [];
imageTypes.forEach((type) => {

    patterns.push({
        from: '*.jpg',
        to: `assets/images/picks/${type.folder}/`,
    });

});

mix.imagemin(
    patterns,
    {
        context: 'resources/assets/images/picks',
    },
    {
        progressive: true,
        svgoPlugins: [{removeViewBox: false}],
        use: [pngquant],
    },
);
bradley-varol commented 5 years ago

Excellent! Thank you for explaining. I misunderstood the use of the patterns argument.
I'll get this implemented today and let you know if I run into any other issues. Cheers.

leonp5 commented 4 years ago

Sorry for using this issue for my question.

I adopted the proposal from @CupOfTea696 but i don't get, why it doesn't compressing the images.

Can somebody help me out here? It looks like this:

require('laravel-mix-imagemin');

const imageFolders = [
    {folder: 'landingpage'},
    {folder: 'unterordner2'}
];

let patterns = [];
imageFolders.forEach((type) => {

    patterns.push({
        from: `${type.folder}/*.*`,
        to: `assets/images/`,
    });

});

mix.imagemin(
    patterns,
    {
        context: 'frontend/images',
    },
    {
        optipng: {
            optimizationLevel: 5
        },
        jpegtran: null,
        plugins: [
            require('imagemin-mozjpeg')({
                quality: 50,
                progressive: true,
            }),
        ],
    }
)

Edit

I'm ending up with this solution for now.

mix.webpackConfig({
    plugins: [
        new CopyWebpackPlugin({
                patterns: [
                    {
                        from: 'frontend/images/', to: 'assets/images/'
                    }
                ],
            },
        ),
        new ImageminPlugin({
            // disable: process.env.NODE_ENV !== 'production', // Disable during development
            pngquant: ({quality: [0.5, 0.5]}),
            plugins: [imageminMozjpeg({quality: 50})],
            test: /\.(jpe?g|png|gif|svg)$/i,
        }),
    ],
});

In my case a additional trap was, that some of the test pictures were already compressed. So nothing to minimize there.