UNPKG

23.7 kBJavaScriptView Raw
1/*!
2 * The MIT License (MIT)
3 *
4 * Copyright (c) 2019 Mark van Seventer
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy of
7 * this software and associated documentation files (the "Software"), to deal in
8 * the Software without restriction, including without limitation the rights to
9 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10 * the Software, and to permit persons to whom the Software is furnished to do so,
11 * subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24// Strict mode.
25'use strict'
26
27// Package modules.
28const pick = require('lodash.pick')
29const sharp = require('sharp')
30const yargs = require('yargs')
31
32// Local modules.
33const constants = require('./constants')
34const pkg = require('../package.json')
35const queue = require('./queue')
36
37// Configure.
38const IS_TEXT_TERMINAL = process.stdin.isTTY
39
40// Options.
41const global = 'Global Options'
42const input = 'Input Options'
43const optimize = 'Optimization Options'
44const output = 'Output Options'
45
46const globalOptions = {
47 // @see https://sharp.pixelplumbing.com/api-constructor/
48 input: {
49 alias: 'i',
50 defaultDescription: 'stdin',
51 demand: IS_TEXT_TERMINAL,
52 desc: 'Path to (an) image file(s)',
53 group: global,
54 implies: 'output',
55 type: 'array'
56 },
57
58 // @see https://sharp.pixelplumbing.com/api-output/
59 output: {
60 alias: 'o',
61 defaultDescription: 'stdout',
62 demand: IS_TEXT_TERMINAL,
63 desc: 'Directory or URI template to write the image files to',
64 group: global,
65 type: 'string'
66 },
67
68 // @see https://sharp.pixelplumbing.com/api-output#timeout
69 timeout: {
70 desc: 'Number of seconds after which processing will be stopped',
71 group: global,
72 type: 'number'
73 }
74}
75
76// @see https://sharp.pixelplumbing.com/api-constructor
77const inputOptions = {
78 animated: {
79 desc: 'Read all frames/pages of an animated image',
80 group: input,
81 type: 'boolean'
82 },
83 failOn: {
84 choices: constants.FAIL_ON,
85 defaultDescription: 'warning',
86 desc: 'Level of sensitivity to invalid images',
87 group: input
88 },
89 density: {
90 desc: 'DPI for vector images',
91 defaultDescription: 72,
92 group: input,
93 type: 'number'
94 },
95 ignoreIcc: {
96 default: false,
97 desc: 'Should the embedded ICC profile, if any, be ignored',
98 group: input,
99 type: 'boolean'
100 },
101 level: {
102 desc: 'Level to extract from a multi-level input (OpenSlide), zero based',
103 defaultDescription: 0,
104 group: input,
105 type: 'number'
106 },
107 limitInputPixels: {
108 defaultDescription: 0x3FFF * 0x3FFF,
109 desc: 'Do not process input images where the number of pixels (width x height) exceeds this limit',
110 group: input,
111 type: 'number'
112 },
113 page: {
114 defaultDescription: 0,
115 desc: 'Page number to start extracting from for multi-page input',
116 group: input,
117 type: 'number'
118 },
119 pages: {
120 defaultDescription: 1,
121 desc: 'Number of pages to extract for multi-page input',
122 group: input,
123 type: 'number'
124 },
125 sequentialRead: {
126 default: false,
127 desc: 'Use sequential rather than random access where possible',
128 group: input,
129 type: 'boolean'
130 },
131 subifd: {
132 defaultDescription: -1,
133 desc: 'subIFD to extract for OME-TIFF',
134 group: input,
135 type: 'number'
136 },
137 unlimited: {
138 desc: 'Remove safety features that help prevent memory exhaustion',
139 group: input,
140 type: 'boolean'
141 }
142}
143
144const outputOptions = {
145 // @see https://sharp.pixelplumbing.com/api-output#png
146 compressionLevel: {
147 alias: 'c',
148 desc: 'zlib compression level',
149 defaultDescription: 6,
150 group: output,
151 type: 'number'
152 },
153
154 // @see https://sharp.pixelplumbing.com/api-output#toformat
155 format: {
156 alias: 'f',
157 choices: constants.FORMAT,
158 defaultDescription: 'input',
159 desc: 'Force output to a given format',
160 group: output
161 },
162
163 // @see https://sharp.pixelplumbing.com/api-output#withmetadata
164 metadata: {
165 alias: ['m', 'withMetadata'],
166 desc: 'Include all metadata (EXIF, XMP, IPTC) from the input image in the output image',
167 group: output,
168 type: 'boolean'
169 },
170 'metadata.density': {
171 desc: 'Number of pixels per inch (DPI)',
172 group: output,
173 type: 'number'
174 },
175 'metadata.exif': {
176 defaultDescription: '{}',
177 desc: 'Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data',
178 group: output,
179 type: 'object'
180 },
181 'metadata.icc': {
182 defaultDescription: 'sRGB',
183 desc: 'Filesystem path to output ICC profile',
184 group: output,
185 type: 'string'
186 },
187 'metadata.orientation': {
188 desc: 'Used to update the EXIF Orientation tag',
189 group: output,
190 type: 'number'
191 },
192
193 // @see https://sharp.pixelplumbing.com/api-output#jpeg
194 // @see https://sharp.pixelplumbing.com/api-output#png
195 progressive: {
196 alias: 'p',
197 desc: 'Use progressive (interlace) scan',
198 group: output,
199 type: 'boolean'
200 },
201
202 // @see https://sharp.pixelplumbing.com/api-output#avif
203 // @see https://sharp.pixelplumbing.com/api-output#jpeg
204 // @see https://sharp.pixelplumbing.com/api-output#tiff
205 // @see https://sharp.pixelplumbing.com/api-output#webp
206 quality: {
207 alias: 'q',
208 desc: 'Quality',
209 defaultDescription: '80',
210 group: output,
211 type: 'number'
212 }
213}
214
215const optimizationOptions = {
216 // @see https://sharp.pixelplumbing.com/api-output#png
217 adaptiveFiltering: {
218 desc: 'Use adaptive row filtering',
219 group: optimize,
220 type: 'boolean'
221 },
222
223 // @see https://sharp.pixelplumbing.com/api-output#webp
224 alphaQuality: {
225 desc: 'Quality of alpha layer',
226 defaultDescription: '80',
227 group: optimize,
228 type: 'number'
229 },
230
231 // @see https://sharp.pixelplumbing.com/api-output#tiff
232 bitdepth: {
233 choices: [1, 2, 4, 8],
234 defaultDescription: 8,
235 desc: 'Reduce bitdepth to 1, 2, or 4 bit',
236 group: optimize
237 },
238
239 // @see https://sharp.pixelplumbing.com/api-output#avif
240 // @see https://sharp.pixelplumbing.com/api-output#jpeg
241 chromaSubsampling: {
242 desc: 'Set to "4:4:4" to prevent chroma subsampling when quality <= 90',
243 defaultDescription: '4:4:4 (AVIF) / 4:2:0',
244 group: optimize,
245 type: 'string'
246 },
247
248 // @see https://sharp.pixelplumbing.com/api-output#gif
249 // @see https://sharp.dimens.io/api-output#png
250 colors: {
251 alias: 'colours',
252 defaultDescription: 256,
253 desc: 'Maximum number of palette entries',
254 group: optimize,
255 type: 'number'
256 },
257
258 // @see https://sharp.pixelplumbing.com/api-output#tiff
259 compression: {
260 choices: constants.TIFF_COMPRESSION,
261 default: 'jpeg',
262 desc: 'Compression options',
263 group: optimize
264 },
265
266 // @see https://sharp.pixelplumbing.com/api-output#gif
267 delay: {
268 desc: 'Delay(s) between animation frames',
269 group: optimize,
270 type: 'number'
271 },
272
273 // @see https://sharp.pixelplumbing.com/api-output#gif
274 // @see https://sharp.dimens.io/api-output#png
275 dither: {
276 desc: 'Level of Floyd-Steinberg error diffusion',
277 defaultDescription: '1.0',
278 group: optimize,
279 type: 'number'
280 },
281
282 // @see https://sharp.pixelplumbing.com/api-output#avif
283 // @see https://sharp.pixelplumbing.com/api-output#gif
284 // @see https://sharp.pixelplumbing.com/api-output#heif
285 // @see https://sharp.pixelplumbing.com/api-output#png
286 // @see https://sharp.pixelplumbing.com/api-output#webp
287 effort: {
288 defaultDescription: '7 (GIF, PNG) / 4',
289 desc: 'Level of CPU effort to reduce file size',
290 group: optimize,
291 type: 'number'
292 },
293
294 // @see https://sharp.pixelplumbing.com/api-output#heif
295 hbitdepth: {
296 choices: [8, 10, 12],
297 defaultDescription: 8,
298 desc: 'Set bitdepth to 8, 10, or 12 bit',
299 group: optimize
300 },
301
302 // @see https://sharp.pixelplumbing.com/api-output#heif
303 hcompression: {
304 choices: constants.HEIF_COMPRESSION,
305 default: 'av1',
306 desc: 'Compression format',
307 group: optimize
308 },
309
310 // @see https://sharp.pixelplumbing.com/api-output#gif
311 interFrameMaxError: {
312 desc: 'Maximum inter-frame error for transparency',
313 group: optimize,
314 type: 'number'
315 },
316
317 // @see https://sharp.pixelplumbing.com/api-output#gif
318 interPaletteMaxError: {
319 desc: 'Maximum inter-palette error for palette reuse',
320 group: optimize,
321 type: 'number'
322 },
323
324 // @see https://sharp.pixelplumbing.com/api-output#gif
325 loop: {
326 default: 0,
327 desc: 'Number of animation iterations',
328 group: optimize,
329 type: 'number'
330 },
331
332 // @see https://sharp.pixelplumbing.com/api-output#avif
333 // @see https://sharp.pixelplumbing.com/api-output#webp
334 lossless: {
335 desc: 'Use lossless compression mode',
336 group: optimize,
337 type: 'boolean'
338 },
339
340 // @see https://sharp.pixelplumbing.com/api-output#tiff
341 miniswhite: {
342 desc: 'Write 1-bit images as miniswhite',
343 group: optimize,
344 type: 'boolean'
345 },
346
347 // @see https://sharp.pixelplumbing.com/api-output#webp
348 minSize: {
349 desc: 'Prevent use of animation key frames to minimize file size',
350 group: optimize,
351 type: 'boolean'
352 },
353
354 // @see https://sharp.pixelplumbing.com/api-output#webp
355 mixed: {
356 desc: 'Allow mixture of lossy and lossless animation frames',
357 group: optimize,
358 type: 'boolean'
359 },
360
361 // @see https://sharp.pixelplumbing.com/api-output#jpeg
362 mozjpeg: {
363 desc: 'Use mozjpeg defaults',
364 group: optimize,
365 type: 'boolean'
366 },
367
368 // @see https://sharp.pixelplumbing.com/api-output#webp
369 nearLossless: {
370 desc: 'Use near_lossless compression mode',
371 group: optimize,
372 type: 'boolean'
373 },
374
375 // @see https://sharp.pixelplumbing.com/api-output#jpeg
376 optimise: {
377 alias: 'optimize',
378 desc: 'Apply optimiseScans, overshootDeringing, and trellisQuantisation',
379 group: optimize,
380 type: 'boolean'
381 },
382
383 // @see https://sharp.pixelplumbing.com/api-output#jpeg
384 optimiseCoding: {
385 alias: 'optimizeCoding',
386 default: true,
387 desc: 'Optimise Huffman coding tables',
388 group: optimize,
389 type: 'boolean'
390 },
391
392 // @see https://sharp.pixelplumbing.com/api-output#jpeg
393 optimiseScans: {
394 alias: 'optimizeScans',
395 desc: 'Optimise progressive scans',
396 group: optimize,
397 implies: 'progressive',
398 type: 'boolean'
399 },
400
401 // @see https://sharp.pixelplumbing.com/api-output#jpeg
402 overshootDeringing: {
403 desc: 'Apply overshoot deringing',
404 group: optimize,
405 type: 'boolean'
406 },
407
408 // @see https://sharp.dimens.io/api-output#png
409 palette: {
410 desc: 'Quantise to a palette-based image with alpha transparency support',
411 group: optimize,
412 type: 'boolean'
413 },
414
415 // @see https://sharp.pixelplumbing.com/api-output#tiff
416 predictor: {
417 choices: constants.TIFF_PREDICTOR,
418 default: 'horizontal',
419 desc: 'Compression predictor',
420 group: optimize
421 },
422
423 // @see https://sharp.pixelplumbing.com/api-output#webp
424 preset: {
425 choices: constants.PRESETS,
426 default: 'default',
427 desc: 'Named preset for preprocessing/filtering',
428 group: optimize
429 },
430
431 // @see https://sharp.pixelplumbing.com/api-output#tiff
432 pyramid: {
433 desc: 'Write an image pyramid',
434 group: optimize,
435 type: 'boolean'
436 },
437
438 // @see https://sharp.pixelplumbing.com/api-output#jpeg
439 quantisationTable: {
440 alias: 'quantizationTable',
441 defaultDescription: '0',
442 desc: 'Quantization table to use',
443 group: optimize,
444 type: 'number'
445 },
446
447 // @see https://sharp.pixelplumbing.com/api-output#gif
448 reuse: {
449 alias: ['reoptimise', 'reoptimize'],
450 desc: 'Always generate new palettes (slow)',
451 group: optimize,
452 type: 'boolean'
453 },
454
455 // @see https://sharp.pixelplumbing.com/api-output
456 resolutionUnit: {
457 choices: constants.RESOLUTION_UNIT,
458 defaultDescription: 'inch',
459 desc: 'Resolution unit',
460 group: optimize
461 },
462
463 // @see https://sharp.pixelplumbing.com/api-output#webp
464 smartSubsample: {
465 desc: 'High quality chroma subsampling',
466 group: optimize,
467 type: 'boolean'
468 },
469
470 // @see https://sharp.pixelplumbing.com/api-output#tile
471 tileBackground: {
472 defaultDescription: 'rgba(255, 255, 255, 1)',
473 desc: 'Background colour, parsed by the color module',
474 group: optimize,
475 type: 'string'
476 },
477
478 // @see https://sharp.pixelplumbing.com/api-output#tiff
479 tileHeight: {
480 desc: 'Vertical tile size',
481 group: optimize,
482 type: 'number'
483 },
484
485 // @see https://sharp.pixelplumbing.com/api-output#tiff
486 tileWidth: {
487 desc: 'Horizontal tile size',
488 group: optimize,
489 type: 'number'
490 },
491
492 // @see https://sharp.pixelplumbing.com/api-output#jpeg
493 trellisQuantisation: {
494 desc: 'Apply trellis quantisation',
495 group: optimize,
496 type: 'boolean'
497 },
498
499 // @see https://sharp.pixelplumbing.com/api-output#tiff
500 xres: {
501 defaultDescription: '1.0',
502 desc: 'Horizontal resolution',
503 group: optimize,
504 type: 'number'
505 },
506
507 // @see https://sharp.pixelplumbing.com/api-output#tiff
508 yres: {
509 defaultDescription: '1.0',
510 desc: 'Vertical resolution',
511 group: optimize,
512 type: 'number'
513 }
514}
515
516const options = {
517 ...globalOptions,
518 ...inputOptions,
519 ...outputOptions,
520 ...optimizationOptions
521}
522
523// Configure.
524const cli = yargs
525 .parserConfiguration({ 'populate--': true })
526 .strict()
527 .usage('$0 <options> [command..]')
528 .options(options)
529 .example('$0 -i ./input.jpg -o ./out resize 300 200', 'out/input.jpg will be a 300 pixels wide and 200 pixels high image containing a scaled and cropped version of input.jpg')
530 .example('$0 -i ./input.jpg -o ./out -mq90 rotate 180 -- resize 300 -- flatten "#ff6600" -- composite ./overlay.png --gravity southeast -- sharpen', 'out/input.jpg will be an upside down, 300px wide, alpha channel flattened onto orange background, composited with overlay.png with SE gravity, sharpened, with metadata, 90% quality version of input.jpg')
531 .example('$0 -i ./input.jpg -o ./out --metadata', 'Include all metadata in the output image')
532 .example('$0 -i ./input.jpg -o ./out --metadata.exif.IFD0.Copyright "Wernham Hogg"', 'Set "IFD0-Copyright" in output EXIF metadata')
533 .example('$0 -i ./input.jpg -o ./out --metadata.density 96', 'Set output metadata to 96 DPI')
534 .epilog('For more information on available options, please visit https://sharp.pixelplumbing.com/')
535 .showHelpOnFail(false)
536 .wrap(100)
537
538 // Built-in options.
539 .help().alias('help', 'h')
540 .version(pkg.version).alias('version', 'v')
541 .group(['help', 'version'], 'Misc. Options')
542
543 // Commands.
544 // Avoid `yargs.commandDir()` as it uses insertion order, not alphabetical.
545 .command(require('../cmd/operations/affine'))
546 .command(require('../cmd/channel-manipulation/bandbool'))
547 .command(require('../cmd/operations/blur'))
548 .command(require('../cmd/operations/boolean'))
549 .command(require('../cmd/operations/clahe'))
550 .command(require('../cmd/compositing/composite'))
551 .command(require('../cmd/operations/convolve'))
552 .command(require('../cmd/channel-manipulation/ensure-alpha'))
553 .command(require('../cmd/resizing/extend'))
554 .command(require('../cmd/resizing/extract'))
555 .command(require('../cmd/channel-manipulation/extract-channel'))
556 .command(require('../cmd/operations/flatten'))
557 .command(require('../cmd/operations/flip'))
558 .command(require('../cmd/operations/flop'))
559 .command(require('../cmd/operations/gamma'))
560 .command(require('../cmd/colour-manipulation/greyscale'))
561 .command(require('../cmd/channel-manipulation/join-channel'))
562 .command(require('../cmd/operations/linear'))
563 .command(require('../cmd/operations/median'))
564 .command(require('../cmd/operations/modulate'))
565 .command(require('../cmd/operations/negate'))
566 .command(require('../cmd/operations/normalise'))
567 .command(require('../cmd/colour-manipulation/pipeline-colourspace'))
568 .command(require('../cmd/operations/recomb'))
569 .command(require('../cmd/channel-manipulation/remove-alpha'))
570 .command(require('../cmd/resizing/resize'))
571 .command(require('../cmd/operations/rotate'))
572 .command(require('../cmd/operations/sharpen'))
573 .command(require('../cmd/operations/threshold'))
574 .command(require('../cmd/colour-manipulation/tint'))
575 .command(require('../cmd/output'))
576 .command(require('../cmd/colour-manipulation/tocolourspace'))
577 .command(require('../cmd/resizing/trim'))
578 .command(require('../cmd/operations/unflatten'))
579
580// Helpers.
581const originalParse = cli.parse.bind(cli)
582const promisifiedParse = (...args) => {
583 return new Promise((resolve, reject) => {
584 originalParse(...args, (err, argv, output) => {
585 if (err) {
586 reject(err)
587 }
588 if (argv.v || argv.help) {
589 reject(output)
590 }
591 resolve(argv)
592 })
593 })
594}
595
596// Exports.
597module.exports = cli
598module.exports.inputOptions = Object.keys(inputOptions)
599module.exports.parse = function recursiveParse (args, context = {}) {
600 return promisifiedParse(args, context).then(argv => {
601 // Handle arguments.
602 // NOTE Use queue.unshift to apply global options first.
603
604 // Global options.
605
606 // Require at least one input file.
607 // NOTE: check here b/c https://github.com/yargs/yargs/issues/403
608 if (argv.input && argv.input.length === 0) {
609 throw new Error('Not enough arguments following: i, input')
610 }
611
612 // @see https://sharp.pixelplumbing.com/api-output#timeout
613 if (argv.timeout) {
614 queue.unshift(['timeout', (sharp) => sharp.timeout({ seconds: argv.timeout })])
615 }
616
617 // Output options.
618
619 // @see https://sharp.pixelplumbing.com/api-output#toformat
620 if (argv.format) {
621 queue.unshift(['format', (sharp) => sharp.toFormat(argv.format, { compression: argv.hcompression })])
622 }
623
624 // @see https://sharp.pixelplumbing.com/api-output#withmetadata
625 if (argv.metadata) {
626 queue.unshift(['withMetadata', (sharp) => sharp.withMetadata(argv.metadata)])
627 }
628
629 // @see https://sharp.pixelplumbing.com/api-output#heif
630 const { heif } = sharp.format
631 if (argv.hcompression !== optimizationOptions.hcompression.default || // HEIF-specific.
632 // Ensure libheif is installed before applying generic options.
633 (heif.input && heif.input.file &&
634 (argv.effort || argv.hbitdepth || argv.lossless || argv.quality))) {
635 queue.unshift(['heif', (sharp) => {
636 return sharp.heif({
637 bitdepth: argv.hbitdepth,
638 compression: argv.hcompression,
639 effort: argv.effort,
640 force: false,
641 lossless: argv.lossless,
642 quality: argv.quality
643 })
644 }])
645 }
646
647 // @see https://sharp.pixelplumbing.com/api-output#avif
648 if (argv.chromaSubsampling || argv.effort || argv.lossless || argv.quality) {
649 queue.unshift(['avif', (sharp) => {
650 return sharp.avif({
651 chromaSubsampling: argv.chromaSubsampling,
652 effort: argv.effort,
653 force: false,
654 lossless: argv.lossless,
655 quality: argv.quality
656 })
657 }])
658 }
659
660 // @see https://sharp.pixelplumbing.com/api-output#gif
661 if (argv.colors || argv.effort || argv.dither || argv.interFrameMaxError ||
662 argv.interPaletteMaxError || argv.loop || argv.delay || argv.progressive || argv.reuse) {
663 queue.unshift(['gif', (sharp) => {
664 return sharp.gif({
665 colors: argv.colors,
666 force: false,
667 effort: argv.effort,
668 dither: argv.dither,
669 interFrameMaxError: argv.interFrameMaxError,
670 interPaletteMaxError: argv.interPaletteMaxError,
671 loop: argv.loop,
672 delay: argv.delay,
673 progressive: argv.progressive,
674 reuse: argv.reuse
675 })
676 }])
677 }
678
679 // @see https://sharp.pixelplumbing.com/api-output#jpeg
680 if (
681 argv.chromaSubsampling ||
682 argv.mozjpeg ||
683 argv.optimise ||
684 argv.optimiseCoding !== true ||
685 argv.optimiseScans ||
686 argv.overshootDeringing ||
687 argv.progressive ||
688 argv.quantisationTable ||
689 argv.quality ||
690 argv.trellisQuantisation
691 ) {
692 queue.unshift(['jpeg', (sharp) => {
693 return sharp.jpeg({
694 chromaSubsampling: argv.chromaSubsampling,
695 force: false,
696 mozjpeg: argv.mozjpeg,
697 optimiseCoding: argv.optimiseCoding,
698 optimiseScans: argv.optimise || argv.optimiseScans,
699 overshootDeringing: argv.optimise || argv.overshootDeringing,
700 progressive: argv.progressive,
701 quality: argv.quality,
702 quantisationTable: argv.quantisationTable,
703 trellisQuantisation: argv.optimise || argv.trellisQuantisation
704 })
705 }])
706 }
707
708 // @see https://sharp.pixelplumbing.com/api-output#png
709 if (argv.adaptiveFiltering || argv.colors || argv.compressionLevel ||
710 argv.dither || argv.effort || argv.palette || argv.progressive) {
711 queue.unshift(['png', (sharp) => {
712 return sharp.png({
713 adaptiveFiltering: argv.adaptiveFiltering,
714 colors: argv.colors,
715 compressionLevel: argv.compressionLevel,
716 dither: argv.dither,
717 effort: argv.effort,
718 force: false,
719 palette: argv.palette,
720 progressive: argv.progressive
721 })
722 }])
723 }
724
725 // @see https://sharp.pixelplumbing.com/api-output#tiff
726 if (argv.bitdepth ||
727 argv.compression !== optimizationOptions.compression.default ||
728 argv.predictor !== optimizationOptions.predictor.default ||
729 argv.miniswhite || argv.pyramid || argv.quality || argv.resolutionUnit ||
730 argv.tileBackground || argv.tileHeight || argv.tileWidth || argv.xres || argv.yres) {
731 queue.unshift(['tiff', (sharp) => {
732 return sharp.tiff({
733 background: argv.tileBackground,
734 bitdepth: argv.bitdepth,
735 compression: argv.compression,
736 force: false,
737 miniswhite: argv.miniswhite,
738 predictor: argv.predictor,
739 pyramid: argv.pyramid,
740 quality: argv.quality,
741 resolutionUnit: argv.resolutionUnit,
742 tile: argv.tileWidth !== undefined || argv.tileHeight !== undefined,
743 tileHeight: argv.tileHeight || argv.tileWidth,
744 tileWidth: argv.tileWidth || argv.tileHeight,
745 xres: argv.xres,
746 yres: argv.yres
747 })
748 }])
749 }
750
751 // @see https://sharp.pixelplumbing.com/api-output#webp
752 if (argv.alphaQuality || argv.quality || argv.lossless || argv.minSize ||
753 argv.mixed || argv.nearLossless || argv.effort ||
754 argv.preset !== optimizationOptions.preset.default || argv.smartSubsample) {
755 queue.unshift(['webp', (sharp) => {
756 return sharp.webp({
757 alphaQuality: argv.alphaQuality,
758 effort: argv.effort,
759 force: false,
760 lossless: argv.lossless,
761 minSize: argv.minSize,
762 mixed: argv.mixed,
763 nearLossless: argv.nearLossless,
764 preset: argv.preset,
765 quality: argv.quality,
766 smartSubsample: argv.smartSubsample
767 })
768 }])
769 }
770
771 // Invoke with remaining arguments (if any).
772 const { '--': remainingargv = [] } = argv
773 if (remainingargv.length > 0) {
774 return recursiveParse(remainingargv, {
775 ...context,
776 ...pick(argv, Object.keys(options)) // Retain options.
777 })
778 }
779 return argv
780 })
781}