@zhaoshijun/compress 1.2.4 → 1.3.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/bin/compress.js +34 -42
- package/package.json +1 -1
- package/src/config/defaults.js +1 -1
- package/src/core/compressor.js +51 -0
package/bin/compress.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import chalk from 'chalk';
|
|
@@ -9,7 +9,7 @@ import fg from 'fast-glob';
|
|
|
9
9
|
import prettyBytes from 'pretty-bytes';
|
|
10
10
|
import { MultiBar, Presets } from 'cli-progress';
|
|
11
11
|
import { loadConfig } from '../src/config/loader.js';
|
|
12
|
-
import { compressImage } from '../src/core/compressor.js';
|
|
12
|
+
import { compressImage, addWatermarkOnly } from '../src/core/compressor.js';
|
|
13
13
|
import { DEFAULT_EXCLUDES } from '../src/utils/constants.js';
|
|
14
14
|
import { isSupportedImage as isSupported } from '../src/utils/file-utils.js';
|
|
15
15
|
import { createRequire } from 'module';
|
|
@@ -29,21 +29,6 @@ program
|
|
|
29
29
|
.option('--dry-run', '仅列出将处理的文件,不写入磁盘')
|
|
30
30
|
.option('-q, --quiet', '静默模式,仅输出错误')
|
|
31
31
|
.option('-w, --watermark', '启用水印')
|
|
32
|
-
.option('--watermark-text <text>', '水印文本')
|
|
33
|
-
.option('--watermark-opacity <opacity>', '水印透明度 (0-1)', parseFloat)
|
|
34
|
-
.option('--watermark-density <density>', '水印密度 (1-10)', parseInt)
|
|
35
|
-
.option('--watermark-color <color>', '水印颜色 (hex 或 rgba)')
|
|
36
|
-
.option('--watermark-mode <mode>', '水印模式 (text/image/tiled)')
|
|
37
|
-
.option('--watermark-image <path>', '水印图片路径')
|
|
38
|
-
.option('--watermark-x <x>', '水印 X 坐标', parseInt)
|
|
39
|
-
.option('--watermark-y <y>', '水印 Y 坐标', parseInt)
|
|
40
|
-
.option('--watermark-width <width>', '水印宽度', parseInt)
|
|
41
|
-
.option('--watermark-height <height>', '水印高度', parseInt)
|
|
42
|
-
.option('--watermark-position <position>', '水印位置 (top-left/top-right/bottom-left/bottom-right/center)')
|
|
43
|
-
.option('--watermark-padding <padding>', '水印边距', parseInt)
|
|
44
|
-
.option('--watermark-angle <angle>', '水印倾斜角度', parseInt)
|
|
45
|
-
.option('--watermark-spacing-x <spacingX>', '水印水平间隔', parseInt)
|
|
46
|
-
.option('--watermark-spacing-y <spacingY>', '水印垂直间隔', parseInt)
|
|
47
32
|
.action(async (options) => {
|
|
48
33
|
try {
|
|
49
34
|
const startTime = Date.now();
|
|
@@ -54,26 +39,6 @@ program
|
|
|
54
39
|
// 合并命令行选项
|
|
55
40
|
if (options.backup !== undefined) config.backup = options.backup;
|
|
56
41
|
|
|
57
|
-
// 合并水印选项
|
|
58
|
-
if (options.watermark) {
|
|
59
|
-
config.watermark = config.watermark || {};
|
|
60
|
-
if (options.watermarkText) config.watermark.text = options.watermarkText;
|
|
61
|
-
if (options.watermarkOpacity !== undefined) config.watermark.opacity = options.watermarkOpacity;
|
|
62
|
-
if (options.watermarkDensity !== undefined) config.watermark.density = options.watermarkDensity;
|
|
63
|
-
if (options.watermarkColor) config.watermark.color = options.watermarkColor;
|
|
64
|
-
if (options.watermarkMode) config.watermark.mode = options.watermarkMode;
|
|
65
|
-
if (options.watermarkImage) config.watermark.imagePath = options.watermarkImage;
|
|
66
|
-
if (options.watermarkX !== undefined) config.watermark.x = options.watermarkX;
|
|
67
|
-
if (options.watermarkY !== undefined) config.watermark.y = options.watermarkY;
|
|
68
|
-
if (options.watermarkWidth !== undefined) config.watermark.width = options.watermarkWidth;
|
|
69
|
-
if (options.watermarkHeight !== undefined) config.watermark.height = options.watermarkHeight;
|
|
70
|
-
if (options.watermarkPosition) config.watermark.position = options.watermarkPosition;
|
|
71
|
-
if (options.watermarkPadding !== undefined) config.watermark.padding = options.watermarkPadding;
|
|
72
|
-
if (options.watermarkAngle !== undefined) config.watermark.angle = options.watermarkAngle;
|
|
73
|
-
if (options.watermarkSpacingX !== undefined) config.watermark.spacingX = options.watermarkSpacingX;
|
|
74
|
-
if (options.watermarkSpacingY !== undefined) config.watermark.spacingY = options.watermarkSpacingY;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
42
|
// 确定输入源
|
|
78
43
|
// 如果 config.input 未定义,则默认为当前目录 '**/*.{jpg,jpeg,png,webp}'
|
|
79
44
|
// 需要结合排除规则
|
|
@@ -251,17 +216,25 @@ program
|
|
|
251
216
|
console.log(chalk.green('Created compress.config.js'));
|
|
252
217
|
});
|
|
253
218
|
|
|
254
|
-
program.parse();
|
|
255
|
-
|
|
256
219
|
// Watermark command
|
|
257
220
|
program
|
|
258
221
|
.command('watermark')
|
|
259
222
|
.description('给图片添加水印(不压缩)')
|
|
260
223
|
.option('-c, --config <path>', '配置文件路径')
|
|
224
|
+
.option('--watermark-mode <mode>', '水印模式 (text/image/tiled)', 'text')
|
|
261
225
|
.option('--watermark-text <text>', '水印文本')
|
|
226
|
+
.option('--watermark-image <path>', '水印图片路径')
|
|
262
227
|
.option('--watermark-opacity <opacity>', '水印透明度 (0-1)', parseFloat)
|
|
263
228
|
.option('--watermark-density <density>', '水印密度 (1-10)', parseInt)
|
|
264
229
|
.option('--watermark-color <color>', '水印颜色 (hex 或 rgba)')
|
|
230
|
+
.option('--watermark-font-size <size>', '水印字体大小', parseInt)
|
|
231
|
+
.option('--watermark-width <width>', '水印图片宽度', parseInt)
|
|
232
|
+
.option('--watermark-height <height>', '水印图片高度', parseInt)
|
|
233
|
+
.option('--watermark-position <position>', '水印位置 (top-left/top-right/bottom-left/bottom-right/center/custom)')
|
|
234
|
+
.option('--watermark-padding <padding>', '水印边距', parseInt)
|
|
235
|
+
.option('--watermark-angle <angle>', '水印倾斜角度', parseInt)
|
|
236
|
+
.option('--watermark-spacing-x <spacing>', '水印水平间隔', parseInt)
|
|
237
|
+
.option('--watermark-spacing-y <spacing>', '水印垂直间隔', parseInt)
|
|
265
238
|
.option('--output <dir>', '输出目录(默认为 ./watermarked)')
|
|
266
239
|
.action(async (options) => {
|
|
267
240
|
try {
|
|
@@ -271,13 +244,30 @@ program
|
|
|
271
244
|
const config = await loadConfig(options.config);
|
|
272
245
|
|
|
273
246
|
// 合并水印选项
|
|
247
|
+
if (options.watermarkMode) config.watermark.mode = options.watermarkMode;
|
|
274
248
|
if (options.watermarkText) config.watermark.text = options.watermarkText;
|
|
249
|
+
if (options.watermarkImage) config.watermark.imagePath = options.watermarkImage;
|
|
275
250
|
if (options.watermarkOpacity !== undefined) config.watermark.opacity = options.watermarkOpacity;
|
|
276
251
|
if (options.watermarkDensity !== undefined) config.watermark.density = options.watermarkDensity;
|
|
277
252
|
if (options.watermarkColor) config.watermark.color = options.watermarkColor;
|
|
253
|
+
if (options.watermarkFontSize) config.watermark.fontSize = options.watermarkFontSize;
|
|
254
|
+
if (options.watermarkWidth) config.watermark.width = options.watermarkWidth;
|
|
255
|
+
if (options.watermarkHeight) config.watermark.height = options.watermarkHeight;
|
|
256
|
+
if (options.watermarkPosition) config.watermark.position = options.watermarkPosition;
|
|
257
|
+
if (options.watermarkPadding) config.watermark.padding = options.watermarkPadding;
|
|
258
|
+
if (options.watermarkAngle) config.watermark.angle = options.watermarkAngle;
|
|
259
|
+
if (options.watermarkSpacingX) config.watermark.spacingX = options.watermarkSpacingX;
|
|
260
|
+
if (options.watermarkSpacingY) config.watermark.spacingY = options.watermarkSpacingY;
|
|
278
261
|
|
|
279
|
-
|
|
280
|
-
|
|
262
|
+
// 验证水印配置
|
|
263
|
+
const mode = config.watermark.mode || 'text';
|
|
264
|
+
if (mode === 'text' && !config.watermark.text) {
|
|
265
|
+
console.error(chalk.red('Error: Watermark text is required for text mode. Use --watermark-text option or set it in config file.'));
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if ((mode === 'image' || mode === 'tiled') && !config.watermark.imagePath) {
|
|
270
|
+
console.error(chalk.red('Error: Watermark image path is required for image/tiled mode. Use --watermark-image option or set it in config file.'));
|
|
281
271
|
process.exit(1);
|
|
282
272
|
}
|
|
283
273
|
|
|
@@ -334,7 +324,7 @@ program
|
|
|
334
324
|
const outDir = path.join(outputDir, relDir);
|
|
335
325
|
const outFilePath = path.join(outDir, fileName);
|
|
336
326
|
|
|
337
|
-
const watermarkedBuffer = await
|
|
327
|
+
const watermarkedBuffer = await addWatermarkOnly(file, config, file);
|
|
338
328
|
|
|
339
329
|
// 写入文件
|
|
340
330
|
await fs.ensureDir(outDir);
|
|
@@ -371,3 +361,5 @@ program
|
|
|
371
361
|
process.exit(1);
|
|
372
362
|
}
|
|
373
363
|
});
|
|
364
|
+
|
|
365
|
+
program.parse();
|
package/package.json
CHANGED
package/src/config/defaults.js
CHANGED
package/src/core/compressor.js
CHANGED
|
@@ -2,6 +2,57 @@ import sharp from 'sharp';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { addWatermark, addImageWatermark, addTiledImageWatermark } from './watermark.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* 只添加水印(不压缩)
|
|
7
|
+
* @param {string|Buffer} input - 图片路径或 Buffer
|
|
8
|
+
* @param {Object} options - 水印配置
|
|
9
|
+
* @param {string} [filePath] - 原始文件路径 (用于判断格式)
|
|
10
|
+
* @returns {Promise<Buffer>} 添加水印后的 Buffer
|
|
11
|
+
*/
|
|
12
|
+
export async function addWatermarkOnly(input, options, filePath) {
|
|
13
|
+
// 初始化 sharp 实例
|
|
14
|
+
let instance = sharp(input);
|
|
15
|
+
const metadata = await instance.metadata();
|
|
16
|
+
|
|
17
|
+
// 添加水印
|
|
18
|
+
if (options.watermark) {
|
|
19
|
+
// 文本水印
|
|
20
|
+
if (options.watermark.mode === 'text' || (options.watermark.text && options.watermark.text.trim() !== '')) {
|
|
21
|
+
const watermarkedBuffer = await addWatermark(instance, options.watermark, metadata);
|
|
22
|
+
instance = sharp(watermarkedBuffer);
|
|
23
|
+
}
|
|
24
|
+
// 单个图片水印
|
|
25
|
+
else if (options.watermark.mode === 'image' || (options.watermark.imagePath && options.watermark.mode !== 'tiled')) {
|
|
26
|
+
const watermarkedBuffer = await addImageWatermark(instance, options.watermark);
|
|
27
|
+
instance = sharp(watermarkedBuffer);
|
|
28
|
+
}
|
|
29
|
+
// 平铺图片水印
|
|
30
|
+
else if (options.watermark.mode === 'tiled' || (options.watermark.imagePath && options.watermark.mode === 'tiled')) {
|
|
31
|
+
const watermarkedBuffer = await addTiledImageWatermark(instance, options.watermark, metadata);
|
|
32
|
+
instance = sharp(watermarkedBuffer);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 保持原始格式,不进行压缩
|
|
37
|
+
const format = metadata.format;
|
|
38
|
+
switch (format) {
|
|
39
|
+
case 'jpeg':
|
|
40
|
+
case 'jpg':
|
|
41
|
+
instance = instance.jpeg({ quality: 100 });
|
|
42
|
+
break;
|
|
43
|
+
case 'png':
|
|
44
|
+
instance = instance.png({ compressionLevel: 0 });
|
|
45
|
+
break;
|
|
46
|
+
case 'webp':
|
|
47
|
+
instance = instance.webp({ quality: 100 });
|
|
48
|
+
break;
|
|
49
|
+
default:
|
|
50
|
+
throw new Error(`Unsupported format: ${format}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return instance.toBuffer();
|
|
54
|
+
}
|
|
55
|
+
|
|
5
56
|
/**
|
|
6
57
|
* 压缩图片
|
|
7
58
|
* @param {string|Buffer} input - 图片路径或 Buffer
|