Klathmon / imagemin-webpack-plugin

Plugin to compress images with imagemin

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

externalImages.destination doesn't work when externalImages.sources are absolute paths

eamodio opened this issue · comments

If you pass absolute paths to externalImages.sources, the destination seems to be completely ignored and the files are overwritten in place.

I can look at this later today hopefully, but a bit more info if you can.

When you say "absolute paths" what do you mean? Like path.resolve('.', 'image', 'thing.jpg') absolute, '/home/klathmon/projects/things/images/thing.jpg', or something else?

I was trying the 2nd case. I see that is it calling path.resolve(dest, file), so if want to save the new files into a new folder, I can't since it won't resolve to something else.

The reason I was trying absolute paths, is I found that this plugin doesn't respect the --context <folder> setting on webpack, it seems to just use the current working directory

Well not respecting context is a bug in itself!

I'll take a look and see what I can do here.

Thanks! Really appreciate the quick response!

Another thing I noticed (and I don't know if has anything to do with this plugin or not), but if I run webpack directly it takes about 4s to optimize a folder of images, BUT if it run the same exact webpack command through npm it takes over 40s for the same set of images. I've added some logging and it looks like the optimize itself is taking longer for some reason.

Also from looking at the code, I think you are reading the file contents twice if it isn't in the cache -- once on 151 and again on 159

return map(invokeIfFunction(sources), (filename) => throttle(async () => {
const fileData = await readFile(filename)
if (testFunction(filename, fileData)) {
const writeFilePath = path.resolve(invokedDestination, filename)
// Use the helper function to get the file from cache if possible, or
// run the optimize function and store it in the cache when done
let optimizedImageBuffer = await getFromCacheIfPossible(cacheFolder, filename, async () => {
return optimizeImage(await readFile(filename), this.options.imageminOptions)
})
// Write the file to the destination when done
return writeFile(writeFilePath, optimizedImageBuffer)
}

Good catch! It seems in my refactoring I managed to double-read the file!

I can fix that one along with this.

Oh I see! It's all because of context...

Since path.resolve actually works a bit differently than I thought, it finds the first "absolute" path possible going from right to left in the arguments, and uses that ignoring the rest (although it's not like it would have worked even if path.resolve worked the way I thought it did...)

Since you passed in an absolute path for the source element, path.resolve takes that absolue path, and just uses that as the return value right away, and never cares about the destination!

So in other words, you called it right away, but I didn't really read and fully understand your comment...

So I need to follow context, and then do some unholy work with path.relative to get it working the way I want even with absolute paths...

Until then, you can work around it by using path.join combined with __dirname instead (EX: path.join(__dirname, 'images', 'stuff.jpg') or however you need to get there (i think, maybe, there actually might be ways that it's impossible for someone to use externalImages right now...)

Either way, I think i'll have time tomorrow morning to not only follow context, but also fix this bug correctly.

Thanks! I tried lots of variations to get it to work, but the only way I was able to get it to work both with and without --context was to process.chdir(__dirname); in the destination function -- certainly not a good thing, but it got me through for right now.

Okay I just published v2.0.0-0 as a beta. If you could install imagemin-webpack-plugin@2.0.0-0 and let me know if it works for you. I tested it as well as I could here, but I just want to make sure it works well for you. (Don't forget to check out the readme for the new context option in the externalImages object in case you can't or don't want to use webpack's context)

If everything is good, just let me know and I can publish it properly.

And thanks again for the bug report, there were a ton of ugly bugs hiding in there...

Awesome -- thank you!

Using 2.0, I got it to work with:

externalImages: {
    sources: glob.sync(path.resolve(__dirname, 'images/settings/*.png')),
    destination: path.resolve(__dirname, '../..')
},

I should mention though that the slowness when run via npm run <command-that-runs-webpack> still persists. I tried playing with the maxConcurrency and that didn't do much (other than make it slow in both cases). It looks like for some reason when run from npm, it won't spawn more than 1 optimization executable, but I can figure out a rhyme or reason why.

npm says it runs command in cmd.exe on Windows and I tried running it from a cmd shell and I don't see the slowness -- only via npm run

Any ideas? One additional thought was maybe the async-throttle is causing an issue? I noticed it is deprecated, but who knows.

That's really interesting. I don't see that at all, and I always run things from npm run ( and more often yarn run).

Were you able to verify that it's only starting one process? There should be one imagemin process for every image up to maxConcurrency running in the task manager on windows (you can verify by just trying to minify like 20 big images at once).

And yeah, async-throttle is deprecated, but I've locked the version long ago, and it's working fine for our use case. At some point I will get around to replacing it, but it's not high on the priority list since it has been working fine this far...

Yeah, its strange. Just did a bit more testing -- if I use npm run to run a script (webpack) and the package.json and webpack.config.js are in the same folder everything is nice and fast. BUT if I use my root package.json to npm run a script (e.g. pushd \"./src/ui\" && webpack && popd or webpack --context ./src/ui --config ./src/ui/webpack.config.js) then it is SLOW.

I added some console.logs and see that you are kicking things off concurrently (up to maxConcurrency), but watching the spawned processes they are only starting 1 at a time

Dump your process.env object, and look for anything majorly different.

If the plugin is running the optimizeImage function "concurrently" then it has to be a problem with imagemin (or more accurately one of the imagemin plugins, since imagemin is only an interface to the plugins).

Closing this as I haven't heard back. Let me know if it's still and issue and if you were able to figure anything else out.