pug-site-core 2.0.11 → 2.0.12

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.
Files changed (3) hide show
  1. package/index.js +7 -0
  2. package/lib/imagemin.js +182 -0
  3. package/package.json +13 -4
package/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { startDevServer } from "./lib/devServer.js";
2
2
  import { translateLanguageData } from "./lib/translate.js";
3
+ import { compressImages, processImagemin } from "./lib/imagemin.js";
3
4
  import {
4
5
  generateGetDataFn,
5
6
  compilePagesPugToFn,
@@ -16,6 +17,7 @@ export const pugSiteCore = {
16
17
  buildFn,
17
18
  buildStatic,
18
19
  translateLanguageData,
20
+ compressImages,
19
21
  };
20
22
 
21
23
  let curCmd = process.env.npm_lifecycle_event;
@@ -49,6 +51,11 @@ if (args.includes("dev")) {
49
51
  case "lang":
50
52
  await translateLanguageData();
51
53
  break;
54
+ case "imagemin":
55
+ // 移除 dev 参数后传递其他参数
56
+ const imageminArgs = args.filter((arg) => arg !== "dev");
57
+ await processImagemin(imageminArgs);
58
+ break;
52
59
  default:
53
60
  console.log(`未知的命令: ${curCmd}`);
54
61
  }
@@ -0,0 +1,182 @@
1
+ import imagemin from 'imagemin';
2
+ import imageminMozjpeg from 'imagemin-mozjpeg';
3
+ import imageminPngquant from 'imagemin-pngquant';
4
+ import imageminGifsicle from 'imagemin-gifsicle';
5
+ import imageminSvgo from 'imagemin-svgo';
6
+ import { glob } from 'glob';
7
+ import path from 'path';
8
+ import fse from 'fs-extra';
9
+ import { paths } from './paths.js';
10
+
11
+ /**
12
+ * 压缩图片
13
+ * @param {string} inputPath - 输入路径,相对于项目根目录
14
+ * @param {number} quality - 压缩质量 (0-1)
15
+ * @returns {Promise<void>}
16
+ */
17
+ export async function compressImages(inputPath, quality = 0.7) {
18
+ try {
19
+ console.log('开始压缩图片...');
20
+ const startTime = Date.now();
21
+
22
+ // 默认路径为静态资源目录下的所有图片
23
+ const defaultPath = paths.template.static;
24
+ const targetPath = inputPath ? paths.resolveRoot(inputPath) : paths.resolveRoot(defaultPath);
25
+ if (!fse.existsSync(targetPath)) {
26
+ throw new Error(`路径不存在: ${targetPath}`);
27
+ }
28
+
29
+ // 将质量值限制在0-1之间
30
+ quality = Math.max(0, Math.min(1, quality));
31
+ console.log(`压缩质量: ${quality}`);
32
+
33
+ // 获取所有图片文件
34
+ const imagePatterns = [
35
+ '**/*.jpg',
36
+ '**/*.jpeg',
37
+ '**/*.png',
38
+ '**/*.gif',
39
+ '**/*.svg'
40
+ ];
41
+
42
+ let imageFiles = [];
43
+ for (const pattern of imagePatterns) {
44
+ const files = await glob(pattern, { cwd: targetPath, absolute: true });
45
+ imageFiles = [...imageFiles, ...files];
46
+ }
47
+
48
+ if (imageFiles.length === 0) {
49
+ console.log('未找到图片文件');
50
+ return;
51
+ }
52
+
53
+ console.log(`找到 ${imageFiles.length} 个图片文件`);
54
+
55
+ // 按目录分组处理图片
56
+ const filesByDir = imageFiles.reduce((acc, file) => {
57
+ const dir = path.dirname(file);
58
+ if (!acc[dir]) acc[dir] = [];
59
+ acc[dir].push(file);
60
+ return acc;
61
+ }, {});
62
+
63
+ // 处理每个目录中的图片
64
+ for (const [dir, files] of Object.entries(filesByDir)) {
65
+ const relativeDir = path.relative(targetPath, dir);
66
+ // 记录原始文件大小
67
+ const originalSizes = {};
68
+ let validFiles = [];
69
+
70
+ for (const file of files) {
71
+ const stats = await fse.stat(file);
72
+ if (stats.size > 0) {
73
+ originalSizes[file] = stats.size;
74
+ validFiles.push(file);
75
+ }
76
+ }
77
+
78
+ // 如果没有有效文件,跳过此目录
79
+ if (validFiles.length === 0) {
80
+ continue;
81
+ }
82
+
83
+ console.log(`处理目录: ${relativeDir || '.'}`);
84
+
85
+ // 压缩图片
86
+ await imagemin(validFiles, {
87
+ destination: dir,
88
+ plugins: [
89
+ imageminMozjpeg({ quality: quality * 100 }),
90
+ imageminPngquant({
91
+ quality: [Math.max(0.3, quality - 0.2), Math.min(0.9, quality + 0.1)]
92
+ }),
93
+ imageminGifsicle({ optimizationLevel: 3 }),
94
+ imageminSvgo({
95
+ plugins: [
96
+ {
97
+ name: 'preset-default',
98
+ params: {
99
+ overrides: {
100
+ removeViewBox: false
101
+ }
102
+ }
103
+ }
104
+ ]
105
+ })
106
+ ]
107
+ });
108
+
109
+ // 计算压缩结果
110
+ let totalOriginalSize = 0;
111
+ let totalCompressedSize = 0;
112
+ let processedFiles = 0;
113
+
114
+ for (const file of validFiles) {
115
+ const originalSize = originalSizes[file];
116
+ const stats = await fse.stat(file);
117
+ const compressedSize = stats.size;
118
+
119
+ if (originalSize > 0 && compressedSize > 0) {
120
+ totalOriginalSize += originalSize;
121
+ totalCompressedSize += compressedSize;
122
+ processedFiles++;
123
+
124
+ const savingPercent = ((originalSize - compressedSize) / originalSize * 100).toFixed(2);
125
+ console.log(` ${path.basename(file)}: ${formatBytes(originalSize)} → ${formatBytes(compressedSize)} (节省 ${savingPercent}%)`);
126
+ }
127
+ }
128
+
129
+ // 只有当有处理过的文件时才打印总结
130
+ if (processedFiles > 0) {
131
+ const totalSavingPercent = ((totalOriginalSize - totalCompressedSize) / totalOriginalSize * 100).toFixed(2);
132
+ console.log(` 目录总计: ${formatBytes(totalOriginalSize)} → ${formatBytes(totalCompressedSize)} (节省 ${totalSavingPercent}%)`);
133
+ }
134
+ }
135
+
136
+ const endTime = Date.now();
137
+ console.log(`图片压缩完成,共处理 ${imageFiles.length} 个文件,耗时 ${(endTime - startTime) / 1000} 秒`);
138
+ } catch (error) {
139
+ console.error('图片压缩失败:', error);
140
+ throw error;
141
+ }
142
+ }
143
+
144
+ /**
145
+ * 格式化字节大小
146
+ * @param {number} bytes - 字节数
147
+ * @returns {string} 格式化后的大小
148
+ */
149
+ function formatBytes(bytes) {
150
+ if (bytes === 0) return '0 B';
151
+
152
+ const sizes = ['B', 'KB', 'MB', 'GB'];
153
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
154
+
155
+ return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${sizes[i]}`;
156
+ }
157
+
158
+ /**
159
+ * 解析命令行参数并执行图片压缩
160
+ * @param {string[]} args - 命令行参数
161
+ * @returns {Promise<void>}
162
+ */
163
+ export async function processImagemin(args) {
164
+ try {
165
+ // 解析图片压缩参数
166
+ let inputPath = null;
167
+ let quality = 0.7; // 默认压缩比例
168
+
169
+ for (const arg of args) {
170
+ if (arg.startsWith('p=')) {
171
+ inputPath = arg.substring(2);
172
+ } else if (arg.startsWith('q=')) {
173
+ quality = parseFloat(arg.substring(2));
174
+ }
175
+ }
176
+
177
+ await compressImages(inputPath, quality);
178
+ } catch (error) {
179
+ console.error('执行失败:', error);
180
+ throw error;
181
+ }
182
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pug-site-core",
3
- "version": "2.0.11",
3
+ "version": "2.0.12",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -11,6 +11,7 @@
11
11
  "buildFn": "node index.js",
12
12
  "buildStatic": "node index.js",
13
13
  "lang": "node index.js",
14
+ "imagemin": "node index.js",
14
15
  "build": "npm run getData && npm run buildFn",
15
16
  "update": "npm install pug-site-core@latest"
16
17
  },
@@ -19,6 +20,7 @@
19
20
  "dependencies": {
20
21
  "@google-cloud/translate": "^8.5.0",
21
22
  "async": "^3.2.6",
23
+ "axios": "^1.7.7",
22
24
  "chokidar": "^3.6.0",
23
25
  "express": "^4.19.2",
24
26
  "express-useragent": "^1.0.15",
@@ -34,16 +36,23 @@
34
36
  "pug": "^3.0.3",
35
37
  "tcp-port-used": "^1.0.2",
36
38
  "uglify-js": "^3.19.3",
37
- "ws": "^8.18.0",
38
- "axios": "^1.7.7"
39
+ "ws": "^8.18.0"
39
40
  },
40
41
  "license": "ISC",
41
- "description": "取消buildFn打包需要jsonData目录",
42
+ "description": "增加图片压缩功能",
42
43
  "files": [
43
44
  "lib/",
44
45
  "index.js"
45
46
  ],
46
47
  "exports": {
47
48
  ".": "./index.js"
49
+ },
50
+ "devDependencies": {
51
+ "glob": "^10.3.10",
52
+ "imagemin": "^8.0.1",
53
+ "imagemin-gifsicle": "^7.0.0",
54
+ "imagemin-mozjpeg": "^10.0.0",
55
+ "imagemin-pngquant": "^9.0.2",
56
+ "imagemin-svgo": "^10.0.1"
48
57
  }
49
58
  }