@mediaproc/image 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2516 -0
- package/bin/cli.js +6 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +89 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/auto-enhance.d.ts +3 -0
- package/dist/commands/auto-enhance.d.ts.map +1 -0
- package/dist/commands/auto-enhance.js +148 -0
- package/dist/commands/auto-enhance.js.map +1 -0
- package/dist/commands/batch.d.ts +3 -0
- package/dist/commands/batch.d.ts.map +1 -0
- package/dist/commands/batch.js +230 -0
- package/dist/commands/batch.js.map +1 -0
- package/dist/commands/blur.d.ts +3 -0
- package/dist/commands/blur.d.ts.map +1 -0
- package/dist/commands/blur.js +122 -0
- package/dist/commands/blur.js.map +1 -0
- package/dist/commands/border.d.ts +3 -0
- package/dist/commands/border.d.ts.map +1 -0
- package/dist/commands/border.js +122 -0
- package/dist/commands/border.js.map +1 -0
- package/dist/commands/clahe.d.ts +3 -0
- package/dist/commands/clahe.d.ts.map +1 -0
- package/dist/commands/clahe.js +121 -0
- package/dist/commands/clahe.js.map +1 -0
- package/dist/commands/composite.d.ts +3 -0
- package/dist/commands/composite.d.ts.map +1 -0
- package/dist/commands/composite.js +167 -0
- package/dist/commands/composite.js.map +1 -0
- package/dist/commands/convert.d.ts +3 -0
- package/dist/commands/convert.d.ts.map +1 -0
- package/dist/commands/convert.js +136 -0
- package/dist/commands/convert.js.map +1 -0
- package/dist/commands/convolve.d.ts +3 -0
- package/dist/commands/convolve.d.ts.map +1 -0
- package/dist/commands/convolve.js +171 -0
- package/dist/commands/convolve.js.map +1 -0
- package/dist/commands/crop.d.ts +3 -0
- package/dist/commands/crop.d.ts.map +1 -0
- package/dist/commands/crop.js +120 -0
- package/dist/commands/crop.js.map +1 -0
- package/dist/commands/dominant-color.d.ts +3 -0
- package/dist/commands/dominant-color.d.ts.map +1 -0
- package/dist/commands/dominant-color.js +175 -0
- package/dist/commands/dominant-color.js.map +1 -0
- package/dist/commands/extend.d.ts +3 -0
- package/dist/commands/extend.d.ts.map +1 -0
- package/dist/commands/extend.js +146 -0
- package/dist/commands/extend.js.map +1 -0
- package/dist/commands/extract.d.ts +3 -0
- package/dist/commands/extract.d.ts.map +1 -0
- package/dist/commands/extract.js +169 -0
- package/dist/commands/extract.js.map +1 -0
- package/dist/commands/flip.d.ts +3 -0
- package/dist/commands/flip.d.ts.map +1 -0
- package/dist/commands/flip.js +112 -0
- package/dist/commands/flip.js.map +1 -0
- package/dist/commands/gamma.d.ts +3 -0
- package/dist/commands/gamma.d.ts.map +1 -0
- package/dist/commands/gamma.js +129 -0
- package/dist/commands/gamma.js.map +1 -0
- package/dist/commands/grayscale.d.ts +3 -0
- package/dist/commands/grayscale.d.ts.map +1 -0
- package/dist/commands/grayscale.js +94 -0
- package/dist/commands/grayscale.js.map +1 -0
- package/dist/commands/grid.d.ts +3 -0
- package/dist/commands/grid.d.ts.map +1 -0
- package/dist/commands/grid.js +193 -0
- package/dist/commands/grid.js.map +1 -0
- package/dist/commands/median.d.ts +3 -0
- package/dist/commands/median.d.ts.map +1 -0
- package/dist/commands/median.js +132 -0
- package/dist/commands/median.js.map +1 -0
- package/dist/commands/metadata-cmd.d.ts +3 -0
- package/dist/commands/metadata-cmd.d.ts.map +1 -0
- package/dist/commands/metadata-cmd.js +201 -0
- package/dist/commands/metadata-cmd.js.map +1 -0
- package/dist/commands/mirror.d.ts +3 -0
- package/dist/commands/mirror.d.ts.map +1 -0
- package/dist/commands/mirror.js +218 -0
- package/dist/commands/mirror.js.map +1 -0
- package/dist/commands/modulate.d.ts +3 -0
- package/dist/commands/modulate.d.ts.map +1 -0
- package/dist/commands/modulate.js +151 -0
- package/dist/commands/modulate.js.map +1 -0
- package/dist/commands/negate.d.ts +3 -0
- package/dist/commands/negate.d.ts.map +1 -0
- package/dist/commands/negate.js +111 -0
- package/dist/commands/negate.js.map +1 -0
- package/dist/commands/normalize.d.ts +3 -0
- package/dist/commands/normalize.d.ts.map +1 -0
- package/dist/commands/normalize.js +119 -0
- package/dist/commands/normalize.js.map +1 -0
- package/dist/commands/optimize.d.ts +3 -0
- package/dist/commands/optimize.d.ts.map +1 -0
- package/dist/commands/optimize.js +137 -0
- package/dist/commands/optimize.js.map +1 -0
- package/dist/commands/palette.d.ts +3 -0
- package/dist/commands/palette.d.ts.map +1 -0
- package/dist/commands/palette.js +138 -0
- package/dist/commands/palette.js.map +1 -0
- package/dist/commands/pixelate.d.ts +3 -0
- package/dist/commands/pixelate.d.ts.map +1 -0
- package/dist/commands/pixelate.js +123 -0
- package/dist/commands/pixelate.js.map +1 -0
- package/dist/commands/resize.d.ts +3 -0
- package/dist/commands/resize.d.ts.map +1 -0
- package/dist/commands/resize.js +187 -0
- package/dist/commands/resize.js.map +1 -0
- package/dist/commands/rotate.d.ts +3 -0
- package/dist/commands/rotate.d.ts.map +1 -0
- package/dist/commands/rotate.js +103 -0
- package/dist/commands/rotate.js.map +1 -0
- package/dist/commands/sepia.d.ts +3 -0
- package/dist/commands/sepia.d.ts.map +1 -0
- package/dist/commands/sepia.js +119 -0
- package/dist/commands/sepia.js.map +1 -0
- package/dist/commands/sharpen.d.ts +3 -0
- package/dist/commands/sharpen.d.ts.map +1 -0
- package/dist/commands/sharpen.js +123 -0
- package/dist/commands/sharpen.js.map +1 -0
- package/dist/commands/smart-crop.d.ts +3 -0
- package/dist/commands/smart-crop.d.ts.map +1 -0
- package/dist/commands/smart-crop.js +137 -0
- package/dist/commands/smart-crop.js.map +1 -0
- package/dist/commands/split.d.ts +3 -0
- package/dist/commands/split.d.ts.map +1 -0
- package/dist/commands/split.js +177 -0
- package/dist/commands/split.js.map +1 -0
- package/dist/commands/stack.d.ts +3 -0
- package/dist/commands/stack.d.ts.map +1 -0
- package/dist/commands/stack.js +205 -0
- package/dist/commands/stack.js.map +1 -0
- package/dist/commands/stats.d.ts +3 -0
- package/dist/commands/stats.d.ts.map +1 -0
- package/dist/commands/stats.js +187 -0
- package/dist/commands/stats.js.map +1 -0
- package/dist/commands/thumbnail.d.ts +3 -0
- package/dist/commands/thumbnail.d.ts.map +1 -0
- package/dist/commands/thumbnail.js +131 -0
- package/dist/commands/thumbnail.js.map +1 -0
- package/dist/commands/tint.d.ts +3 -0
- package/dist/commands/tint.d.ts.map +1 -0
- package/dist/commands/tint.js +123 -0
- package/dist/commands/tint.js.map +1 -0
- package/dist/commands/trim.d.ts +3 -0
- package/dist/commands/trim.d.ts.map +1 -0
- package/dist/commands/trim.js +123 -0
- package/dist/commands/trim.js.map +1 -0
- package/dist/commands/vignette.d.ts +3 -0
- package/dist/commands/vignette.d.ts.map +1 -0
- package/dist/commands/vignette.js +139 -0
- package/dist/commands/vignette.js.map +1 -0
- package/dist/commands/watermark.d.ts +3 -0
- package/dist/commands/watermark.d.ts.map +1 -0
- package/dist/commands/watermark.js +172 -0
- package/dist/commands/watermark.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/register.d.ts +5 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +93 -0
- package/dist/register.js.map +1 -0
- package/dist/types.d.ts +91 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/helpFormatter.d.ts +63 -0
- package/dist/utils/helpFormatter.d.ts.map +1 -0
- package/dist/utils/helpFormatter.js +171 -0
- package/dist/utils/helpFormatter.js.map +1 -0
- package/dist/utils/sharp.d.ts +20 -0
- package/dist/utils/sharp.d.ts.map +1 -0
- package/dist/utils/sharp.js +14 -0
- package/dist/utils/sharp.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import { createSharpInstance } from '../utils/sharp.js';
|
|
6
|
+
import { createStandardHelp } from '../utils/helpFormatter.js';
|
|
7
|
+
export function paletteCommand(imageCmd) {
|
|
8
|
+
imageCmd
|
|
9
|
+
.command('palette <input>')
|
|
10
|
+
.description('Extract dominant color palette from image')
|
|
11
|
+
.option('-c, --colors <count>', 'Number of colors to extract 1-10 (default: 5)', parseInt, 5)
|
|
12
|
+
.option('-v, --verbose', 'Verbose output with hex codes')
|
|
13
|
+
.option('--help', 'Display help for palette command')
|
|
14
|
+
.action(async (input, options) => {
|
|
15
|
+
if (options.help) {
|
|
16
|
+
createStandardHelp({
|
|
17
|
+
commandName: 'palette',
|
|
18
|
+
emoji: '🎨',
|
|
19
|
+
description: 'Extract and display the dominant color palette from an image. Perfect for design inspiration, brand color extraction, and theme generation.',
|
|
20
|
+
usage: ['palette <input>', 'palette <input> --colors 8', 'palette <input> -c 3 -v'],
|
|
21
|
+
options: [
|
|
22
|
+
{ flag: '-c, --colors <count>', description: 'Number of colors to extract 1-10 (default: 5)' },
|
|
23
|
+
{ flag: '-v, --verbose', description: 'Show detailed output with hex codes and RGB values' }
|
|
24
|
+
],
|
|
25
|
+
examples: [
|
|
26
|
+
{ command: 'palette photo.jpg', description: 'Extract 5 dominant colors' },
|
|
27
|
+
{ command: 'palette image.png --colors 8', description: 'Extract 8 colors palette' },
|
|
28
|
+
{ command: 'palette brand-logo.jpg -c 3 -v', description: 'Extract 3 brand colors with details' },
|
|
29
|
+
{ command: 'palette artwork.png --colors 10', description: 'Full 10-color palette' }
|
|
30
|
+
],
|
|
31
|
+
additionalSections: [
|
|
32
|
+
{
|
|
33
|
+
title: 'Use Cases',
|
|
34
|
+
items: [
|
|
35
|
+
'Design inspiration - extract colors from photos',
|
|
36
|
+
'Brand identity - get colors from logos',
|
|
37
|
+
'Theme generation - create color schemes',
|
|
38
|
+
'UI/UX design - match website colors',
|
|
39
|
+
'Art analysis - understand color composition',
|
|
40
|
+
'Product design - match product colors'
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
title: 'Output Information',
|
|
45
|
+
items: [
|
|
46
|
+
'Dominant colors ranked by prevalence',
|
|
47
|
+
'RGB values for each color',
|
|
48
|
+
'Hex codes for web/design use',
|
|
49
|
+
'Visual color blocks (in terminal)',
|
|
50
|
+
'Percentage of image coverage'
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
tips: [
|
|
55
|
+
'Use fewer colors (3-5) for cleaner palettes',
|
|
56
|
+
'More colors (8-10) for complex images',
|
|
57
|
+
'Verbose mode shows hex codes for design',
|
|
58
|
+
'Great for finding complementary colors'
|
|
59
|
+
]
|
|
60
|
+
});
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
const spinner = ora('Analyzing image colors...').start();
|
|
64
|
+
try {
|
|
65
|
+
if (!fs.existsSync(input)) {
|
|
66
|
+
spinner.fail(chalk.red(`Input file not found: ${input}`));
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
const colorCount = Math.max(1, Math.min(10, options.colors || 5));
|
|
70
|
+
const metadata = await createSharpInstance(input).metadata();
|
|
71
|
+
const stats = await createSharpInstance(input).stats();
|
|
72
|
+
spinner.succeed(chalk.green('✓ Color palette extracted!\n'));
|
|
73
|
+
console.log(chalk.bold.cyan(`📷 Image: ${path.basename(input)}`));
|
|
74
|
+
console.log(chalk.dim(` Size: ${metadata.width}x${metadata.height}`));
|
|
75
|
+
console.log(chalk.dim(` Format: ${metadata.format?.toUpperCase()}`));
|
|
76
|
+
console.log('');
|
|
77
|
+
console.log(chalk.bold.cyan(`🎨 Dominant Color Palette (${colorCount} colors):\n`));
|
|
78
|
+
// Get dominant color
|
|
79
|
+
if (stats.dominant) {
|
|
80
|
+
const { r, g, b } = stats.dominant;
|
|
81
|
+
const hex = `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`.toUpperCase();
|
|
82
|
+
console.log(chalk.bold(' Primary Dominant Color:'));
|
|
83
|
+
console.log(` ${chalk.rgb(r, g, b)('██████')} RGB(${r}, ${g}, ${b}) ${chalk.dim(hex)}`);
|
|
84
|
+
console.log('');
|
|
85
|
+
}
|
|
86
|
+
// Get channel statistics for additional color information
|
|
87
|
+
if (stats.channels && stats.channels.length >= 3) {
|
|
88
|
+
console.log(chalk.bold(' Channel Analysis:'));
|
|
89
|
+
const channels = ['Red', 'Green', 'Blue'];
|
|
90
|
+
stats.channels.slice(0, 3).forEach((channel, index) => {
|
|
91
|
+
const channelName = channels[index];
|
|
92
|
+
const avgValue = Math.round(channel.mean);
|
|
93
|
+
const color = index === 0 ? [avgValue, 0, 0] : index === 1 ? [0, avgValue, 0] : [0, 0, avgValue];
|
|
94
|
+
console.log(` ${channelName.padEnd(6)}: ${chalk.rgb(color[0], color[1], color[2])('██')} Avg: ${avgValue.toString().padStart(3)} (min: ${channel.min}, max: ${channel.max})`);
|
|
95
|
+
});
|
|
96
|
+
console.log('');
|
|
97
|
+
}
|
|
98
|
+
// Calculate additional color info
|
|
99
|
+
if (stats.channels && stats.channels.length >= 3) {
|
|
100
|
+
const avgR = Math.round(stats.channels[0].mean);
|
|
101
|
+
const avgG = Math.round(stats.channels[1].mean);
|
|
102
|
+
const avgB = Math.round(stats.channels[2].mean);
|
|
103
|
+
console.log(chalk.bold(' Average Image Tone:'));
|
|
104
|
+
const avgHex = `#${avgR.toString(16).padStart(2, '0')}${avgG.toString(16).padStart(2, '0')}${avgB.toString(16).padStart(2, '0')}`.toUpperCase();
|
|
105
|
+
console.log(` ${chalk.rgb(avgR, avgG, avgB)('██████')} RGB(${avgR}, ${avgG}, ${avgB}) ${chalk.dim(avgHex)}`);
|
|
106
|
+
console.log('');
|
|
107
|
+
// Determine overall tone
|
|
108
|
+
const brightness = (avgR + avgG + avgB) / 3;
|
|
109
|
+
const tone = brightness > 200 ? 'Very Light' : brightness > 150 ? 'Light' : brightness > 100 ? 'Medium' : brightness > 50 ? 'Dark' : 'Very Dark';
|
|
110
|
+
console.log(chalk.dim(` Overall Brightness: ${tone} (${Math.round(brightness)}/255)`));
|
|
111
|
+
// Color temperature
|
|
112
|
+
const warmCool = avgR > avgB ? 'Warm' : avgR < avgB ? 'Cool' : 'Neutral';
|
|
113
|
+
console.log(chalk.dim(` Color Temperature: ${warmCool}`));
|
|
114
|
+
// Saturation estimate
|
|
115
|
+
const maxChannel = Math.max(avgR, avgG, avgB);
|
|
116
|
+
const minChannel = Math.min(avgR, avgG, avgB);
|
|
117
|
+
const saturation = maxChannel === 0 ? 0 : ((maxChannel - minChannel) / maxChannel) * 100;
|
|
118
|
+
console.log(chalk.dim(` Saturation: ${saturation.toFixed(1)}%`));
|
|
119
|
+
}
|
|
120
|
+
if (options.verbose) {
|
|
121
|
+
console.log('');
|
|
122
|
+
console.log(chalk.dim('💡 Tip: Use these colors in your designs, websites, or branding!'));
|
|
123
|
+
console.log(chalk.dim(' Copy hex codes directly for CSS/design tools.'));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
spinner.fail(chalk.red('Failed to extract palette'));
|
|
128
|
+
if (options.verbose) {
|
|
129
|
+
console.error(chalk.red('Error details:'), error);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
console.error(chalk.red(error.message));
|
|
133
|
+
}
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=palette.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"palette.js","sourceRoot":"","sources":["../../src/commands/palette.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pixelate.d.ts","sourceRoot":"","sources":["../../src/commands/pixelate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiBzC,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CA6HvD"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import { createSharpInstance } from '../utils/sharp.js';
|
|
6
|
+
import { createStandardHelp } from '../utils/helpFormatter.js';
|
|
7
|
+
export function pixelateCommand(imageCmd) {
|
|
8
|
+
imageCmd
|
|
9
|
+
.command('pixelate <input>')
|
|
10
|
+
.description('Apply pixelate effect (mosaic/retro style)')
|
|
11
|
+
.option('-p, --pixels <size>', 'Pixel size 2-50 (default: 10)', parseInt, 10)
|
|
12
|
+
.option('-o, --output <path>', 'Output file path')
|
|
13
|
+
.option('--dry-run', 'Show what would be done without executing')
|
|
14
|
+
.option('-v, --verbose', 'Verbose output')
|
|
15
|
+
.option('--help', 'Display help for pixelate command')
|
|
16
|
+
.action(async (input, options) => {
|
|
17
|
+
if (options.help) {
|
|
18
|
+
createStandardHelp({
|
|
19
|
+
commandName: 'pixelate',
|
|
20
|
+
emoji: '🎮',
|
|
21
|
+
description: 'Apply pixelate/mosaic effect to images. Perfect for retro gaming aesthetics, privacy protection, and artistic effects.',
|
|
22
|
+
usage: ['pixelate <input>', 'pixelate <input> --pixels 20', 'pixelate <input> -p 5 -o retro.jpg'],
|
|
23
|
+
options: [
|
|
24
|
+
{ flag: '-p, --pixels <size>', description: 'Pixel size 2-50 (default: 10) - larger = more pixelated' },
|
|
25
|
+
{ flag: '-o, --output <path>', description: 'Output file path' },
|
|
26
|
+
{ flag: '--dry-run', description: 'Preview changes without executing' },
|
|
27
|
+
{ flag: '-v, --verbose', description: 'Show detailed output' }
|
|
28
|
+
],
|
|
29
|
+
examples: [
|
|
30
|
+
{ command: 'pixelate photo.jpg', description: 'Apply default pixelation' },
|
|
31
|
+
{ command: 'pixelate face.jpg --pixels 20', description: 'Heavy pixelation for privacy' },
|
|
32
|
+
{ command: 'pixelate image.png -p 5', description: 'Subtle pixel art effect' },
|
|
33
|
+
{ command: 'pixelate game.jpg --pixels 8 -o retro.jpg', description: '8-bit retro gaming style' }
|
|
34
|
+
],
|
|
35
|
+
additionalSections: [
|
|
36
|
+
{
|
|
37
|
+
title: 'Pixel Size Guide',
|
|
38
|
+
items: [
|
|
39
|
+
'2-5 - Subtle pixelation, retains detail',
|
|
40
|
+
'8-12 - Classic 8-bit/16-bit gaming style',
|
|
41
|
+
'15-25 - Strong effect, good for privacy',
|
|
42
|
+
'30-50 - Extreme pixelation, very blocky'
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
title: 'Use Cases',
|
|
47
|
+
items: [
|
|
48
|
+
'Retro/vintage gaming aesthetics',
|
|
49
|
+
'Privacy protection (blur faces/text)',
|
|
50
|
+
'Artistic mosaic effects',
|
|
51
|
+
'Censorship/redaction',
|
|
52
|
+
'8-bit style graphics',
|
|
53
|
+
'Low-resolution vintage look'
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
tips: [
|
|
58
|
+
'Start with 10 and adjust to preference',
|
|
59
|
+
'Larger pixels = stronger effect',
|
|
60
|
+
'Works great for retro game art',
|
|
61
|
+
'Combine with sepia for vintage feel'
|
|
62
|
+
]
|
|
63
|
+
});
|
|
64
|
+
process.exit(0);
|
|
65
|
+
}
|
|
66
|
+
const spinner = ora('Applying pixelate effect...').start();
|
|
67
|
+
try {
|
|
68
|
+
if (!fs.existsSync(input)) {
|
|
69
|
+
spinner.fail(chalk.red(`Input file not found: ${input}`));
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
const pixelSize = Math.max(2, Math.min(50, options.pixels || 10));
|
|
73
|
+
const inputPath = path.parse(input);
|
|
74
|
+
const outputPath = options.output || path.join(inputPath.dir, `${inputPath.name}-pixelated${inputPath.ext}`);
|
|
75
|
+
if (options.verbose) {
|
|
76
|
+
spinner.info(chalk.blue('Configuration:'));
|
|
77
|
+
console.log(chalk.dim(` Input: ${input}`));
|
|
78
|
+
console.log(chalk.dim(` Output: ${outputPath}`));
|
|
79
|
+
console.log(chalk.dim(` Pixel size: ${pixelSize}x${pixelSize}`));
|
|
80
|
+
spinner.start('Processing...');
|
|
81
|
+
}
|
|
82
|
+
if (options.dryRun) {
|
|
83
|
+
spinner.info(chalk.yellow('Dry run mode - no changes will be made'));
|
|
84
|
+
console.log(chalk.green('✓ Would apply pixelation:'));
|
|
85
|
+
console.log(chalk.dim(` Input: ${input}`));
|
|
86
|
+
console.log(chalk.dim(` Pixel size: ${pixelSize}x${pixelSize}`));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const metadata = await createSharpInstance(input).metadata();
|
|
90
|
+
const width = metadata.width;
|
|
91
|
+
const height = metadata.height;
|
|
92
|
+
// Pixelate by shrinking then enlarging with nearest neighbor
|
|
93
|
+
const shrunkWidth = Math.floor(width / pixelSize);
|
|
94
|
+
const shrunkHeight = Math.floor(height / pixelSize);
|
|
95
|
+
await createSharpInstance(input)
|
|
96
|
+
.resize(shrunkWidth, shrunkHeight, {
|
|
97
|
+
kernel: 'nearest'
|
|
98
|
+
})
|
|
99
|
+
.resize(width, height, {
|
|
100
|
+
kernel: 'nearest'
|
|
101
|
+
})
|
|
102
|
+
.toFile(outputPath);
|
|
103
|
+
const outputStats = fs.statSync(outputPath);
|
|
104
|
+
spinner.succeed(chalk.green('✓ Pixelate effect applied successfully!'));
|
|
105
|
+
console.log(chalk.dim(` Input: ${input}`));
|
|
106
|
+
console.log(chalk.dim(` Output: ${outputPath}`));
|
|
107
|
+
console.log(chalk.dim(` Size: ${width}x${height}`));
|
|
108
|
+
console.log(chalk.dim(` Pixel size: ${pixelSize}x${pixelSize}`));
|
|
109
|
+
console.log(chalk.dim(` File size: ${(outputStats.size / 1024).toFixed(2)}KB`));
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
spinner.fail(chalk.red('Failed to apply pixelate effect'));
|
|
113
|
+
if (options.verbose) {
|
|
114
|
+
console.error(chalk.red('Error details:'), error);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
console.error(chalk.red(error.message));
|
|
118
|
+
}
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=pixelate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pixelate.js","sourceRoot":"","sources":["../../src/commands/pixelate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resize.d.ts","sourceRoot":"","sources":["../../src/commands/resize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASzC,wBAAgB,aAAa,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAmMrD"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import { createSharpInstance } from '../utils/sharp.js';
|
|
6
|
+
import { createStandardHelp } from '../utils/helpFormatter.js';
|
|
7
|
+
export function resizeCommand(imageCmd) {
|
|
8
|
+
imageCmd
|
|
9
|
+
.command('resize <input>')
|
|
10
|
+
.description('Resize image to specified dimensions')
|
|
11
|
+
.option('-w, --width <width>', 'Width in pixels', parseInt)
|
|
12
|
+
.option('-h, --height <height>', 'Height in pixels', parseInt)
|
|
13
|
+
.option('-o, --output <path>', 'Output file path')
|
|
14
|
+
.option('-q, --quality <quality>', 'Quality (1-100)', parseInt, 90)
|
|
15
|
+
.option('--fit <fit>', 'Fit mode: cover, contain, fill, inside, outside', 'cover')
|
|
16
|
+
.option('--maintain-aspect-ratio', 'Maintain aspect ratio (default: true)', true)
|
|
17
|
+
.option('--no-maintain-aspect-ratio', 'Do not maintain aspect ratio')
|
|
18
|
+
.option('--position <position>', 'Position for cover/contain: center, top, bottom, left, right, etc.', 'center')
|
|
19
|
+
.option('--background <color>', 'Background color for contain/outside (hex, rgb, or named)', '#ffffff')
|
|
20
|
+
.option('--kernel <kernel>', 'Kernel for resizing: nearest, cubic, mitchell, lanczos2, lanczos3', 'lanczos3')
|
|
21
|
+
.option('--dry-run', 'Show what would be done without executing')
|
|
22
|
+
.option('-v, --verbose', 'Verbose output')
|
|
23
|
+
.option('--help', 'Display help for resize command')
|
|
24
|
+
.action(async (input, options) => {
|
|
25
|
+
// Show help if requested
|
|
26
|
+
if (options.help) {
|
|
27
|
+
createStandardHelp({
|
|
28
|
+
commandName: 'resize',
|
|
29
|
+
emoji: '📐',
|
|
30
|
+
description: 'Resize images to specified dimensions with advanced options and filters. Supports multiple fit modes, quality control, and various output formats.',
|
|
31
|
+
usage: [
|
|
32
|
+
'resize <input> -w <width>',
|
|
33
|
+
'resize <input> -h <height>',
|
|
34
|
+
'resize <input> -w <width> -h <height> [options]'
|
|
35
|
+
],
|
|
36
|
+
options: [
|
|
37
|
+
{ flag: '-w, --width <width>', description: 'Width in pixels (auto if not specified)' },
|
|
38
|
+
{ flag: '-h, --height <height>', description: 'Height in pixels (auto if not specified)' },
|
|
39
|
+
{ flag: '-o, --output <path>', description: 'Output file path (default: <input>-resized.<ext>)' },
|
|
40
|
+
{ flag: '-q, --quality <quality>', description: 'Output quality 1-100 (default: 90)' },
|
|
41
|
+
{ flag: '--fit <mode>', description: 'Fit mode: cover, contain, fill, inside, outside (default: cover)' },
|
|
42
|
+
{ flag: '--maintain-aspect-ratio', description: 'Maintain aspect ratio (default: true)' },
|
|
43
|
+
{ flag: '--no-maintain-aspect-ratio', description: 'Do not maintain aspect ratio (uses fill mode)' },
|
|
44
|
+
{ flag: '--position <position>', description: 'Position: center, top, bottom, left, right, etc. (default: center)' },
|
|
45
|
+
{ flag: '--background <color>', description: 'Background color for contain/outside (default: #ffffff)' },
|
|
46
|
+
{ flag: '--kernel <kernel>', description: 'Resize kernel: nearest, cubic, mitchell, lanczos2, lanczos3 (default: lanczos3)' },
|
|
47
|
+
{ flag: '--dry-run', description: 'Preview changes without executing' },
|
|
48
|
+
{ flag: '-v, --verbose', description: 'Show detailed output' }
|
|
49
|
+
],
|
|
50
|
+
examples: [
|
|
51
|
+
{ command: 'resize photo.jpg -w 800', description: 'Resize to 800px width, auto height' },
|
|
52
|
+
{ command: 'resize image.png -w 1920 -h 1080', description: 'Resize to Full HD dimensions' },
|
|
53
|
+
{ command: 'resize pic.jpg -w 500 -h 500 --fit cover', description: 'Crop to square keeping aspect ratio' },
|
|
54
|
+
{ command: 'resize logo.png -w 200 --fit contain --background transparent', description: 'Fit logo in 200px box' },
|
|
55
|
+
{ command: 'resize photo.jpg -w 1024 -q 80 -o output/resized.jpg', description: 'Resize with custom quality and output path' },
|
|
56
|
+
{ command: 'resize image.png -w 800 --kernel lanczos3 --verbose', description: 'High-quality resize with detailed output' },
|
|
57
|
+
{ command: 'resize test.jpg -w 500 -h 500 --dry-run', description: 'Preview resize without making changes' }
|
|
58
|
+
],
|
|
59
|
+
additionalSections: [
|
|
60
|
+
{
|
|
61
|
+
title: 'Fit Modes Explained',
|
|
62
|
+
items: [
|
|
63
|
+
'cover - Crop to fill dimensions (default)',
|
|
64
|
+
'contain - Fit within dimensions (may add padding)',
|
|
65
|
+
'fill - Stretch to exact dimensions (ignores aspect ratio)',
|
|
66
|
+
'inside - Resize to fit inside dimensions',
|
|
67
|
+
'outside - Resize to fit outside dimensions'
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
title: 'Supported Formats',
|
|
72
|
+
items: [
|
|
73
|
+
'Input: JPG, PNG, WebP, AVIF, TIFF, GIF, SVG',
|
|
74
|
+
'Output: JPG, PNG, WebP, AVIF, TIFF (based on extension)'
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
tips: [
|
|
79
|
+
'Use --dry-run to preview changes before applying',
|
|
80
|
+
'Omit width or height to maintain aspect ratio automatically',
|
|
81
|
+
'Use lanczos3 kernel for best quality (slower) or nearest for speed',
|
|
82
|
+
'WebP and AVIF formats provide better compression than JPG'
|
|
83
|
+
]
|
|
84
|
+
});
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
const spinner = ora('Processing image...').start();
|
|
88
|
+
try {
|
|
89
|
+
// Validate input file
|
|
90
|
+
if (!fs.existsSync(input)) {
|
|
91
|
+
spinner.fail(chalk.red(`Input file not found: ${input}`));
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
// Validate dimensions
|
|
95
|
+
if (!options.width && !options.height) {
|
|
96
|
+
spinner.fail(chalk.red('At least one dimension (width or height) must be specified'));
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
// Generate output path if not provided
|
|
100
|
+
const inputPath = path.parse(input);
|
|
101
|
+
const outputPath = options.output || path.join(process.cwd(), `${inputPath.name}-resized${inputPath.ext}`);
|
|
102
|
+
if (options.verbose) {
|
|
103
|
+
spinner.info(chalk.blue('Configuration:'));
|
|
104
|
+
console.log(chalk.dim(` Input: ${input}`));
|
|
105
|
+
console.log(chalk.dim(` Output: ${outputPath}`));
|
|
106
|
+
console.log(chalk.dim(` Width: ${options.width || 'auto'}`));
|
|
107
|
+
console.log(chalk.dim(` Height: ${options.height || 'auto'}`));
|
|
108
|
+
console.log(chalk.dim(` Quality: ${options.quality}`));
|
|
109
|
+
console.log(chalk.dim(` Fit: ${options.fit}`));
|
|
110
|
+
console.log(chalk.dim(` Maintain aspect ratio: ${options.maintainAspectRatio !== false}`));
|
|
111
|
+
console.log(chalk.dim(` Kernel: ${options.kernel || 'lanczos3'}`));
|
|
112
|
+
spinner.start('Processing...');
|
|
113
|
+
}
|
|
114
|
+
// Dry run mode
|
|
115
|
+
if (options.dryRun) {
|
|
116
|
+
spinner.info(chalk.yellow('Dry run mode - no changes will be made'));
|
|
117
|
+
console.log(chalk.green('✓ Would resize image:'));
|
|
118
|
+
console.log(chalk.dim(` From: ${input}`));
|
|
119
|
+
console.log(chalk.dim(` To: ${outputPath}`));
|
|
120
|
+
console.log(chalk.dim(` Dimensions: ${options.width || 'auto'}x${options.height || 'auto'}`));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
// Get input image metadata
|
|
124
|
+
const metadata = await createSharpInstance(input).metadata();
|
|
125
|
+
if (options.verbose) {
|
|
126
|
+
spinner.text = 'Resizing image...';
|
|
127
|
+
console.log(chalk.dim(` Original size: ${metadata.width}x${metadata.height}`));
|
|
128
|
+
}
|
|
129
|
+
// Build Sharp resize options
|
|
130
|
+
const resizeOptions = {
|
|
131
|
+
width: options.width,
|
|
132
|
+
height: options.height,
|
|
133
|
+
fit: options.fit || 'cover',
|
|
134
|
+
position: options.position || 'center',
|
|
135
|
+
background: options.background || '#ffffff',
|
|
136
|
+
kernel: options.kernel || 'lanczos3',
|
|
137
|
+
withoutEnlargement: false,
|
|
138
|
+
};
|
|
139
|
+
// If maintain aspect ratio is false, use 'fill' fit mode
|
|
140
|
+
if (options.maintainAspectRatio === false) {
|
|
141
|
+
resizeOptions.fit = 'fill';
|
|
142
|
+
}
|
|
143
|
+
// Process image with Sharp
|
|
144
|
+
const pipeline = createSharpInstance(input).resize(resizeOptions);
|
|
145
|
+
// Apply quality settings based on output format
|
|
146
|
+
const outputExt = path.extname(outputPath).toLowerCase();
|
|
147
|
+
if (outputExt === '.jpg' || outputExt === '.jpeg') {
|
|
148
|
+
pipeline.jpeg({ quality: options.quality || 90 });
|
|
149
|
+
}
|
|
150
|
+
else if (outputExt === '.png') {
|
|
151
|
+
pipeline.png({ quality: options.quality || 90, compressionLevel: 9 });
|
|
152
|
+
}
|
|
153
|
+
else if (outputExt === '.webp') {
|
|
154
|
+
pipeline.webp({ quality: options.quality || 90 });
|
|
155
|
+
}
|
|
156
|
+
else if (outputExt === '.avif') {
|
|
157
|
+
pipeline.avif({ quality: options.quality || 90 });
|
|
158
|
+
}
|
|
159
|
+
// Save the resized image
|
|
160
|
+
await pipeline.toFile(outputPath);
|
|
161
|
+
// Get output metadata
|
|
162
|
+
const outputMetadata = await createSharpInstance(outputPath).metadata();
|
|
163
|
+
spinner.succeed(chalk.green('✓ Image resized successfully!'));
|
|
164
|
+
console.log(chalk.dim(` Input: ${input}`));
|
|
165
|
+
console.log(chalk.dim(` Output: ${outputPath}`));
|
|
166
|
+
console.log(chalk.dim(` Original size: ${metadata.width}x${metadata.height}`));
|
|
167
|
+
console.log(chalk.dim(` New size: ${outputMetadata.width}x${outputMetadata.height}`));
|
|
168
|
+
console.log(chalk.dim(` Format: ${outputMetadata.format}`));
|
|
169
|
+
// Show file size info
|
|
170
|
+
const inputStats = fs.statSync(input);
|
|
171
|
+
const outputStats = fs.statSync(outputPath);
|
|
172
|
+
const sizeDiff = ((outputStats.size - inputStats.size) / inputStats.size * 100).toFixed(2);
|
|
173
|
+
console.log(chalk.dim(` Size: ${(inputStats.size / 1024).toFixed(2)}KB → ${(outputStats.size / 1024).toFixed(2)}KB (${sizeDiff > '0' ? '+' : ''}${sizeDiff}%)`));
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
spinner.fail(chalk.red('Failed to resize image'));
|
|
177
|
+
if (options.verbose) {
|
|
178
|
+
console.error(chalk.red('Error details:'), error);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
console.error(chalk.red(error.message));
|
|
182
|
+
}
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=resize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resize.js","sourceRoot":"","sources":["../../src/commands/resize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rotate.d.ts","sourceRoot":"","sources":["../../src/commands/rotate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAezC,wBAAgB,aAAa,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAqGrD"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import { createSharpInstance } from '../utils/sharp.js';
|
|
6
|
+
import { createStandardHelp } from '../utils/helpFormatter.js';
|
|
7
|
+
export function rotateCommand(imageCmd) {
|
|
8
|
+
imageCmd
|
|
9
|
+
.command('rotate <input>')
|
|
10
|
+
.description('Rotate image by specified angle')
|
|
11
|
+
.option('-a, --angle <degrees>', 'Rotation angle in degrees (default: 90)', parseFloat, 90)
|
|
12
|
+
.option('--background <color>', 'Background color for areas outside image (default: transparent)', 'transparent')
|
|
13
|
+
.option('-o, --output <path>', 'Output file path')
|
|
14
|
+
.option('-q, --quality <quality>', 'Quality (1-100)', parseInt, 90)
|
|
15
|
+
.option('--dry-run', 'Show what would be done without executing')
|
|
16
|
+
.option('-v, --verbose', 'Verbose output')
|
|
17
|
+
.option('--help', 'Display help for rotate command')
|
|
18
|
+
.action(async (input, options) => {
|
|
19
|
+
if (options.help) {
|
|
20
|
+
createStandardHelp({
|
|
21
|
+
commandName: 'rotate',
|
|
22
|
+
emoji: '🔄',
|
|
23
|
+
description: 'Rotate images by any angle. Use positive values for clockwise rotation, negative for counter-clockwise.',
|
|
24
|
+
usage: ['rotate <input>', 'rotate <input> -a <angle>', 'rotate <input> -a <angle> --background <color>'],
|
|
25
|
+
options: [
|
|
26
|
+
{ flag: '-a, --angle <degrees>', description: 'Rotation angle in degrees (default: 90)' },
|
|
27
|
+
{ flag: '--background <color>', description: 'Background color for empty areas (default: transparent)' },
|
|
28
|
+
{ flag: '-o, --output <path>', description: 'Output file path (default: <input>-rotated.<ext>)' },
|
|
29
|
+
{ flag: '-q, --quality <quality>', description: 'Output quality 1-100 (default: 90)' },
|
|
30
|
+
{ flag: '--dry-run', description: 'Preview changes without executing' },
|
|
31
|
+
{ flag: '-v, --verbose', description: 'Show detailed output' }
|
|
32
|
+
],
|
|
33
|
+
examples: [
|
|
34
|
+
{ command: 'rotate photo.jpg', description: 'Rotate 90° clockwise (default)' },
|
|
35
|
+
{ command: 'rotate image.png -a 180', description: 'Rotate 180° (upside down)' },
|
|
36
|
+
{ command: 'rotate pic.jpg -a -90', description: 'Rotate 90° counter-clockwise' },
|
|
37
|
+
{ command: 'rotate photo.jpg -a 45', description: 'Rotate 45° clockwise' },
|
|
38
|
+
{ command: 'rotate image.png -a 30 --background white', description: 'Rotate with white background' }
|
|
39
|
+
],
|
|
40
|
+
tips: ['Use transparent background for PNGs', 'Negative angles rotate counter-clockwise']
|
|
41
|
+
});
|
|
42
|
+
process.exit(0);
|
|
43
|
+
}
|
|
44
|
+
const spinner = ora('Processing image...').start();
|
|
45
|
+
try {
|
|
46
|
+
if (!fs.existsSync(input)) {
|
|
47
|
+
spinner.fail(chalk.red(`Input file not found: ${input}`));
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
const inputPath = path.parse(input);
|
|
51
|
+
const outputPath = options.output || path.join(process.cwd(), `${inputPath.name}-rotated${inputPath.ext}`);
|
|
52
|
+
if (options.verbose) {
|
|
53
|
+
spinner.info(chalk.blue('Configuration:'));
|
|
54
|
+
console.log(chalk.dim(` Input: ${input}`));
|
|
55
|
+
console.log(chalk.dim(` Output: ${outputPath}`));
|
|
56
|
+
console.log(chalk.dim(` Angle: ${options.angle || 90}°`));
|
|
57
|
+
console.log(chalk.dim(` Background: ${options.background || 'transparent'}`));
|
|
58
|
+
spinner.start('Processing...');
|
|
59
|
+
}
|
|
60
|
+
if (options.dryRun) {
|
|
61
|
+
spinner.info(chalk.yellow('Dry run mode - no changes will be made'));
|
|
62
|
+
console.log(chalk.green('✓ Would rotate image:'));
|
|
63
|
+
console.log(chalk.dim(` From: ${input}`));
|
|
64
|
+
console.log(chalk.dim(` To: ${outputPath}`));
|
|
65
|
+
console.log(chalk.dim(` Angle: ${options.angle || 90}°`));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const metadata = await createSharpInstance(input).metadata();
|
|
69
|
+
const pipeline = createSharpInstance(input).rotate(options.angle || 90, {
|
|
70
|
+
background: options.background || 'transparent'
|
|
71
|
+
});
|
|
72
|
+
const outputExt = path.extname(outputPath).toLowerCase();
|
|
73
|
+
if (outputExt === '.jpg' || outputExt === '.jpeg') {
|
|
74
|
+
pipeline.jpeg({ quality: options.quality || 90 });
|
|
75
|
+
}
|
|
76
|
+
else if (outputExt === '.png') {
|
|
77
|
+
pipeline.png({ quality: options.quality || 90 });
|
|
78
|
+
}
|
|
79
|
+
else if (outputExt === '.webp') {
|
|
80
|
+
pipeline.webp({ quality: options.quality || 90 });
|
|
81
|
+
}
|
|
82
|
+
await pipeline.toFile(outputPath);
|
|
83
|
+
const outputMetadata = await createSharpInstance(outputPath).metadata();
|
|
84
|
+
spinner.succeed(chalk.green('✓ Image rotated successfully!'));
|
|
85
|
+
console.log(chalk.dim(` Input: ${input}`));
|
|
86
|
+
console.log(chalk.dim(` Output: ${outputPath}`));
|
|
87
|
+
console.log(chalk.dim(` Original size: ${metadata.width}x${metadata.height}`));
|
|
88
|
+
console.log(chalk.dim(` New size: ${outputMetadata.width}x${outputMetadata.height}`));
|
|
89
|
+
console.log(chalk.dim(` Angle: ${options.angle || 90}°`));
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
spinner.fail(chalk.red('Failed to rotate image'));
|
|
93
|
+
if (options.verbose) {
|
|
94
|
+
console.error(chalk.red('Error details:'), error);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
console.error(chalk.red(error.message));
|
|
98
|
+
}
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=rotate.js.map
|