diff --git a/CHANGELOG.md b/CHANGELOG.md index a5494bf..56430ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Added support for HEIF images. * Added [`--premultiplied`](https://sharp.pixelplumbing.com/en/latest/api-composite/#composite) option. * Added [`--reductionEffort` and `--smartSubsample`](https://sharp.pixelplumbing.com/en/stable/api-output/#webp) options. +* Fixed bug with output directories ([#16](https://github.com/vseventer/sharp-cli/issues/16)). * Removed [`overlayWith`](http://sharp.pixelplumbing.com/en/v0.21.3/api-composite/#overlaywith) command. * Updated `fs-extra`, `mocha`, `sharp`, `sinon`, and `standard` dependencies. diff --git a/lib/convert.js b/lib/convert.js index f184857..613e098 100644 --- a/lib/convert.js +++ b/lib/convert.js @@ -29,6 +29,7 @@ const path = require('path') // Package modules. const bubbleError = require('bubble-stream-error') +const isDirectory = require('is-directory') const sharp = require('sharp') const urlTemplate = require('url-template') @@ -49,11 +50,17 @@ module.exports = { // Convert a list of files. files: (input, output) => { const template = urlTemplate.parse(output) // Parse output template. + output = path.resolve(output) // Process files. + const isBatch = input.length > 1 const promises = input.map((src) => { - // Destination. src = path.resolve(src) // Absolute path. + + // Process file. + const transformer = queue.drain(sharp(src)) + + // Destination. let dest = template.expand(path.parse(src)) // Path separator for windows is encoded by url-template. @@ -61,19 +68,16 @@ module.exports = { if (process.platform === 'win32') { dest = dest.split('%5C').join(path.sep) } + dest = path.resolve(dest) // Absolute path. - // Absolute path. - dest = path.resolve(dest) - - // Process file. - const transformer = queue.drain(sharp(src)) - - // If output was not a template, re-use source name. - if (output === dest) { + // If output was not a template, assume dest is a directory when using + // batch processing. + const outputAssumeDir = dest === output && isBatch + if (outputAssumeDir || isDirectory.sync(dest)) { const defaultExt = path.extname(src) const desiredExt = transformer.options.formatOut dest = path.format({ - dir: output, + dir: dest, name: path.basename(src, defaultExt), ext: desiredExt in EXTENSIONS ? EXTENSIONS[desiredExt] : defaultExt }) diff --git a/package-lock.json b/package-lock.json index 3701929..d30682a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1704,6 +1704,11 @@ "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", "dev": true }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", diff --git a/package.json b/package.json index b29d6aa..d3f85be 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ }, "dependencies": { "bubble-stream-error": "1.0.x", + "is-directory": "0.3.x", "multiyargs": "1.0.x", "sharp": "0.23.1", "url-template": "2.0.x", diff --git a/test/convert.js b/test/convert.js index ee6fec7..1ebef96 100644 --- a/test/convert.js +++ b/test/convert.js @@ -55,6 +55,19 @@ describe('convert', () => { .files([input], dest) .then(([info]) => expect(fs.existsSync(info.path)).to.be.true) }) + it('must convert a file and output to an existing directory', () => { + // Negative test for directory that does not exist. + const rand = '' + Math.random() + return convert + .files([input, input], rand) + .then(() => { throw new Error('STOP') }) + .catch((err) => { + expect(err).to.exist() + expect(err).to.have.property('message') + expect(err.message).to.contain(`${rand}/input.jpg`) + expect(err.message).to.contain('No such file or directory') + }) + }) it('must convert multiple files', () => { return convert .files([input, input], dest)