@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 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
- if (!config.watermark.text) {
280
- console.error(chalk.red('Error: Watermark text is required. Use --watermark-text option or set it in config file.'));
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 compressImage(file, config, file);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhaoshijun/compress",
3
- "version": "1.2.4",
3
+ "version": "1.3.0",
4
4
  "description": "Image compression CLI and Vite plugin",
5
5
  "type": "module",
6
6
  "bin": {
@@ -30,7 +30,7 @@ export const defaultConfig = {
30
30
  watermark: {
31
31
  mode: 'text',
32
32
  text: '',
33
- opacity: 0.5,
33
+ opacity: 1,
34
34
  density: 3,
35
35
  color: '#ffffff',
36
36
  fontSize: 24,
@@ -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