@pz4l/tinyimg-unplugin 0.3.2 → 0.3.6

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/dist/index.d.mts CHANGED
@@ -12,6 +12,8 @@ interface TinyimgUnpluginOptions extends FilterOptions {
12
12
  cache?: boolean;
13
13
  parallel?: number;
14
14
  strict?: boolean;
15
+ level?: 'quiet' | 'normal' | 'verbose';
16
+ /** @deprecated use `level: 'verbose'` instead */
15
17
  verbose?: boolean;
16
18
  }
17
19
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/filter.ts","../src/options.ts","../src/index.ts"],"mappings":";;;UAIiB,aAAA;EACf,OAAA;EACA,OAAA;AAAA;;;UCJe,sBAAA,SAA+B,aAAA;EAC9C,IAAA;EACA,KAAA;EACA,QAAA;EACA,MAAA;EACA,OAAA;AAAA;;;cCPqD,QAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/filter.ts","../src/options.ts","../src/index.ts"],"mappings":";;;UAIiB,aAAA;EACf,OAAA;EACA,OAAA;AAAA;;;UCJe,sBAAA,SAA+B,aAAA;EAC9C,IAAA;EACA,KAAA;EACA,QAAA;EACA,MAAA;EACA,KAAA;EDDO;ECGP,OAAA;AAAA;;;cCTqD,QAAA"}
package/dist/index.mjs CHANGED
@@ -1,9 +1,10 @@
1
1
  import { Buffer } from "node:buffer";
2
2
  import process from "node:process";
3
3
  import { compressImage, formatBytes, loadKeys } from "@pz4l/tinyimg-core";
4
+ import path from "pathe";
4
5
  import { createUnplugin } from "unplugin";
5
- import path from "node:path";
6
6
  import micromatch from "micromatch";
7
+ import kleur from "kleur";
7
8
  //#region src/filter.ts
8
9
  const IMAGE_EXTENSIONS = new Set([
9
10
  ".png",
@@ -11,124 +12,20 @@ const IMAGE_EXTENSIONS = new Set([
11
12
  ".jpeg"
12
13
  ]);
13
14
  function shouldProcessImage(id, options = {}) {
14
- const ext = path.extname(id).toLowerCase();
15
+ const normalizedId = path.normalize(id);
16
+ const ext = path.extname(normalizedId).toLowerCase();
15
17
  if (!IMAGE_EXTENSIONS.has(ext)) return false;
16
18
  if (options.include) {
17
19
  const includePatterns = Array.isArray(options.include) ? options.include : [options.include];
18
- if (!micromatch.isMatch(id, includePatterns)) return false;
20
+ if (!micromatch.isMatch(normalizedId, includePatterns)) return false;
19
21
  }
20
22
  if (options.exclude) {
21
23
  const excludePatterns = Array.isArray(options.exclude) ? options.exclude : [options.exclude];
22
- if (micromatch.isMatch(id, excludePatterns)) return false;
24
+ if (micromatch.isMatch(normalizedId, excludePatterns)) return false;
23
25
  }
24
26
  return true;
25
27
  }
26
28
  //#endregion
27
- //#region src/stats.ts
28
- var CompressionStats = class {
29
- compressedCount = 0;
30
- cachedCount = 0;
31
- originalSize = 0;
32
- compressedSize = 0;
33
- fileResults = [];
34
- recordCompressed(path, originalSize, compressedSize) {
35
- this.compressedCount++;
36
- this.originalSize += originalSize;
37
- this.compressedSize += compressedSize;
38
- this.fileResults.push({
39
- path,
40
- originalSize,
41
- compressedSize,
42
- cached: false
43
- });
44
- }
45
- recordCached(path, size) {
46
- this.cachedCount++;
47
- this.originalSize += size;
48
- this.compressedSize += size;
49
- this.fileResults.push({
50
- path,
51
- originalSize: size,
52
- compressedSize: size,
53
- cached: true
54
- });
55
- }
56
- recordError(path, error) {
57
- this.fileResults.push({
58
- path,
59
- originalSize: 0,
60
- cached: false,
61
- error
62
- });
63
- }
64
- getSummary() {
65
- return {
66
- compressedCount: this.compressedCount,
67
- cachedCount: this.cachedCount,
68
- originalSize: this.originalSize,
69
- compressedSize: this.compressedSize,
70
- bytesSaved: this.originalSize - this.compressedSize,
71
- fileCount: this.fileResults.length
72
- };
73
- }
74
- formatSummary() {
75
- const summary = this.getSummary();
76
- const lines = [];
77
- if (summary.compressedCount === 0 && summary.cachedCount > 0) lines.push(`[tinyimg] All images cached (0 compressed, ${summary.cachedCount} cached)`);
78
- else {
79
- lines.push(`✓ [tinyimg] Compressed ${summary.fileCount} images (${summary.cachedCount} cached, ${summary.compressedCount} compressed)`);
80
- lines.push(`✓ [tinyimg] Saved ${formatBytes(summary.bytesSaved)} (original: ${formatBytes(summary.originalSize)} → compressed: ${formatBytes(summary.compressedSize)})`);
81
- }
82
- return lines;
83
- }
84
- getFileResults() {
85
- return this.fileResults;
86
- }
87
- };
88
- //#endregion
89
- //#region src/logger.ts
90
- var TinyimgLogger = class {
91
- stats;
92
- verbose;
93
- strict;
94
- constructor(options = {}) {
95
- this.verbose = options.verbose ?? false;
96
- this.strict = options.strict ?? false;
97
- this.stats = new CompressionStats();
98
- }
99
- logCompressing(path) {
100
- if (this.verbose) console.log(`[tinyimg] Compressing ${path}...`);
101
- }
102
- logCompressed(path, originalSize, compressedSize) {
103
- this.stats.recordCompressed(path, originalSize, compressedSize);
104
- if (this.verbose) {
105
- const saved = ((1 - compressedSize / originalSize) * 100).toFixed(1);
106
- console.log(`[tinyimg] ✓ Compressed: ${formatBytes(originalSize)} → ${formatBytes(compressedSize)} (${saved}% saved)`);
107
- }
108
- }
109
- logCacheHit(path, size) {
110
- this.stats.recordCached(path, size);
111
- if (this.verbose) console.log(`[tinyimg] Cache hit: ${path}`);
112
- }
113
- logError(path, error) {
114
- this.stats.recordError(path, error);
115
- if (this.strict) console.error(`[tinyimg] ✖ Failed to compress ${path}: ${error}. Build failed.`);
116
- else console.warn(`[tinyimg] ⚠ Failed to compress ${path}: ${error}. Using original file.`);
117
- }
118
- logSummary() {
119
- this.stats.formatSummary().forEach((line) => console.log(line));
120
- }
121
- getStats() {
122
- return this.stats;
123
- }
124
- shouldThrowOnError() {
125
- return this.strict;
126
- }
127
- };
128
- function createLogger(options = {}) {
129
- return new TinyimgLogger(options);
130
- }
131
- //#endregion
132
29
  //#region src/options.ts
133
30
  const VALID_MODES = new Set([
134
31
  "random",
@@ -138,26 +35,150 @@ const VALID_MODES = new Set([
138
35
  function normalizeOptions(options = {}) {
139
36
  if (options.mode !== void 0 && !VALID_MODES.has(options.mode)) throw new TypeError(`Invalid mode: "${options.mode}". Must be one of: random, round-robin, priority`);
140
37
  if (options.parallel !== void 0 && options.parallel <= 0) throw new RangeError(`Invalid parallel: ${options.parallel}. Must be a positive number`);
38
+ const level = options.level ?? (options.verbose === true ? "verbose" : "normal");
141
39
  return {
142
40
  mode: options.mode ?? "random",
143
41
  cache: options.cache ?? true,
144
42
  parallel: options.parallel ?? 8,
145
43
  strict: options.strict ?? false,
146
- verbose: options.verbose ?? false,
44
+ level,
147
45
  include: options.include ? Array.isArray(options.include) ? options.include : [options.include] : void 0,
148
46
  exclude: options.exclude ? Array.isArray(options.exclude) ? options.exclude : [options.exclude] : void 0
149
47
  };
150
48
  }
151
49
  //#endregion
50
+ //#region src/utils/logger.ts
51
+ /**
52
+ * 统一的终端日志类(unplugin 专用)
53
+ * 支持 quiet/normal/verbose 三级输出级别
54
+ * 内置统计功能,与 CLI TerminalLogger 对齐
55
+ * 使用统一配色主题(THM-01)
56
+ */
57
+ var TerminalLogger = class {
58
+ level = "normal";
59
+ stats = {
60
+ processed: 0,
61
+ compressed: 0,
62
+ cached: 0,
63
+ errors: 0,
64
+ originalSize: 0,
65
+ compressedSize: 0
66
+ };
67
+ /**
68
+ * 构造函数
69
+ * @param level - 日志级别,默认为 'normal'
70
+ */
71
+ constructor(level = "normal") {
72
+ this.level = level;
73
+ }
74
+ /**
75
+ * 设置日志级别
76
+ */
77
+ setLevel(level) {
78
+ this.level = level;
79
+ }
80
+ /**
81
+ * 获取当前日志级别
82
+ */
83
+ getLevel() {
84
+ return this.level;
85
+ }
86
+ /**
87
+ * 记录压缩成功
88
+ * 格式:✓ {path} {originalSize} → {compressedSize} -{percent}%
89
+ */
90
+ successCompress(path, originalSize, compressedSize) {
91
+ if (this.level === "quiet") return;
92
+ this.stats.processed++;
93
+ this.stats.compressed++;
94
+ this.stats.originalSize += originalSize;
95
+ this.stats.compressedSize += compressedSize;
96
+ const savedPercent = originalSize === 0 ? 0 : (1 - compressedSize / originalSize) * 100;
97
+ console.log(`${kleur.green("✓")} ${path} ${formatBytes(originalSize)} → ${formatBytes(compressedSize)} -${savedPercent.toFixed(1)}%`);
98
+ }
99
+ /**
100
+ * 记录缓存命中
101
+ * 格式:✓ {path} {size} cached
102
+ */
103
+ cacheHit(path, size) {
104
+ if (this.level === "quiet") return;
105
+ this.stats.processed++;
106
+ this.stats.cached++;
107
+ this.stats.originalSize += size;
108
+ this.stats.compressedSize += size;
109
+ console.log(`${kleur.green("✓")} ${path} ${formatBytes(size)} cached`);
110
+ }
111
+ /**
112
+ * 记录压缩错误
113
+ * strict=true: 使用 console.error 输出 ✗ 前缀(红色)
114
+ * strict=false: 使用 console.warn 输出 ⚠ 前缀(黄色)
115
+ */
116
+ errorCompress(path, message, strict) {
117
+ this.stats.processed++;
118
+ this.stats.errors++;
119
+ if (strict) console.error(`${kleur.red("✗")} ${path} ${message}`);
120
+ else console.warn(`${kleur.yellow("⚠")} ${path} ${message}`);
121
+ }
122
+ /**
123
+ * 信息日志 - normal/verbose 模式输出
124
+ * 配色:青色 + ℹ 前缀
125
+ */
126
+ info(message) {
127
+ if (this.level === "quiet") return;
128
+ console.log(`${kleur.cyan("ℹ")} ${message}`);
129
+ }
130
+ /**
131
+ * 详细日志 - 仅 verbose 模式输出
132
+ * 配色:灰色
133
+ */
134
+ verbose(message) {
135
+ if (this.level === "verbose") console.log(kleur.gray(message));
136
+ }
137
+ /**
138
+ * 输出汇总信息
139
+ * 格式与 CLI compress 汇总一致:
140
+ * ✓ Compression complete
141
+ * Files: N processed, M compressed, K cached
142
+ * [Errors: E](如果有错误)
143
+ * Savings: X.X% avg, Y.Z total
144
+ */
145
+ summary() {
146
+ if (this.level === "quiet" || this.stats.processed === 0) return;
147
+ const savedBytes = this.stats.originalSize - this.stats.compressedSize;
148
+ const savedPercent = this.stats.originalSize === 0 ? 0 : savedBytes / this.stats.originalSize * 100;
149
+ console.log(`${kleur.green("✓")} Compression complete`);
150
+ console.log(` Files: ${this.stats.processed} processed, ${this.stats.compressed} compressed, ${this.stats.cached} cached`);
151
+ if (this.stats.errors > 0) console.log(` Errors: ${this.stats.errors}`);
152
+ console.log(` Savings: ${savedPercent.toFixed(1)}% avg, ${formatBytes(savedBytes)} total`);
153
+ }
154
+ /**
155
+ * 获取当前统计信息
156
+ */
157
+ getStats() {
158
+ return { ...this.stats };
159
+ }
160
+ /**
161
+ * 重置统计信息
162
+ */
163
+ resetStats() {
164
+ this.stats = {
165
+ processed: 0,
166
+ compressed: 0,
167
+ cached: 0,
168
+ errors: 0,
169
+ originalSize: 0,
170
+ compressedSize: 0
171
+ };
172
+ }
173
+ };
174
+ new TerminalLogger();
175
+ //#endregion
152
176
  //#region src/index.ts
153
177
  const IMAGE_REGEX = /\.(png|jpg|jpeg|gif|webp|svg)$/i;
154
178
  var src_default = createUnplugin((options = {}) => {
155
179
  const normalized = normalizeOptions(options);
156
180
  if (loadKeys().length === 0) throw new Error("TINYPNG_KEYS environment variable is required");
157
- const logger = createLogger({
158
- verbose: normalized.verbose,
159
- strict: normalized.strict
160
- });
181
+ const logger = new TerminalLogger(normalized.level);
161
182
  return {
162
183
  name: "tinyimg-unplugin",
163
184
  enforce: "post",
@@ -166,26 +187,29 @@ var src_default = createUnplugin((options = {}) => {
166
187
  if (!isProductionBuild(this)) return null;
167
188
  const buffer = Buffer.from(code);
168
189
  const relativePath = getRelativePath(id);
169
- logger.logCompressing(relativePath);
170
190
  try {
171
- const compressed = await compressImage(buffer, {
191
+ const { buffer: compressed, meta } = await compressImage(buffer, {
172
192
  projectCacheOnly: true,
173
193
  cache: normalized.cache,
174
194
  mode: normalized.mode
175
195
  });
176
- logger.logCompressed(relativePath, buffer.length, compressed.length);
196
+ if (meta.cached) logger.cacheHit(relativePath, meta.originalSize);
197
+ else {
198
+ logger.successCompress(relativePath, meta.originalSize, meta.compressedSize);
199
+ if (normalized.level === "verbose" && meta.compressorName) logger.verbose(` compressor: ${meta.compressorName}`);
200
+ }
177
201
  return {
178
202
  code: compressed,
179
203
  map: null
180
204
  };
181
205
  } catch (error) {
182
- logger.logError(relativePath, error.message);
183
- if (logger.shouldThrowOnError()) throw error;
206
+ logger.errorCompress(relativePath, error.message, normalized.strict);
207
+ if (normalized.strict) throw error;
184
208
  return null;
185
209
  }
186
210
  },
187
211
  buildEnd() {
188
- logger.logSummary();
212
+ logger.summary();
189
213
  }
190
214
  };
191
215
  });
@@ -196,7 +220,7 @@ function isProductionBuild(context) {
196
220
  }
197
221
  function getRelativePath(id) {
198
222
  const root = process.cwd();
199
- return id.replace(root, "").replace(IMAGE_REGEX, "");
223
+ return path.relative(root, id).replace(IMAGE_REGEX, "");
200
224
  }
201
225
  //#endregion
202
226
  export { src_default as default };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/filter.ts","../src/stats.ts","../src/logger.ts","../src/options.ts","../src/index.ts"],"sourcesContent":["import path from 'node:path'\n// @ts-expect-error - micromatch doesn't have types\nimport micromatch from 'micromatch'\n\nexport interface FilterOptions {\n include?: string | string[]\n exclude?: string | string[]\n}\n\nexport const IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg'])\n\nexport function shouldProcessImage(id: string, options: FilterOptions = {}): boolean {\n // 1. Check extension first (fast path)\n const ext = path.extname(id).toLowerCase()\n if (!IMAGE_EXTENSIONS.has(ext)) {\n return false\n }\n\n // 2. Check include pattern if provided\n if (options.include) {\n const includePatterns = Array.isArray(options.include) ? options.include : [options.include]\n const isInclude = micromatch.isMatch(id, includePatterns)\n if (!isInclude)\n return false\n }\n\n // 3. Check exclude pattern if provided\n if (options.exclude) {\n const excludePatterns = Array.isArray(options.exclude) ? options.exclude : [options.exclude]\n const isExclude = micromatch.isMatch(id, excludePatterns)\n if (isExclude)\n return false\n }\n\n return true\n}\n","import { formatBytes } from '@pz4l/tinyimg-core'\n\nexport interface FileResult {\n path: string\n originalSize: number\n compressedSize?: number\n cached: boolean\n error?: string\n}\n\nexport class CompressionStats {\n private compressedCount = 0\n private cachedCount = 0\n private originalSize = 0\n private compressedSize = 0\n private fileResults: FileResult[] = []\n\n recordCompressed(path: string, originalSize: number, compressedSize: number): void {\n this.compressedCount++\n this.originalSize += originalSize\n this.compressedSize += compressedSize\n this.fileResults.push({ path, originalSize, compressedSize, cached: false })\n }\n\n recordCached(path: string, size: number): void {\n this.cachedCount++\n this.originalSize += size\n this.compressedSize += size\n this.fileResults.push({ path, originalSize: size, compressedSize: size, cached: true })\n }\n\n recordError(path: string, error: string): void {\n this.fileResults.push({ path, originalSize: 0, cached: false, error })\n }\n\n getSummary() {\n return {\n compressedCount: this.compressedCount,\n cachedCount: this.cachedCount,\n originalSize: this.originalSize,\n compressedSize: this.compressedSize,\n bytesSaved: this.originalSize - this.compressedSize,\n fileCount: this.fileResults.length,\n }\n }\n\n formatSummary(): string[] {\n const summary = this.getSummary()\n const lines: string[] = []\n\n if (summary.compressedCount === 0 && summary.cachedCount > 0) {\n // All cached (D-11)\n lines.push(`[tinyimg] All images cached (0 compressed, ${summary.cachedCount} cached)`)\n }\n else {\n // Normal summary (D-09)\n lines.push(`✓ [tinyimg] Compressed ${summary.fileCount} images (${summary.cachedCount} cached, ${summary.compressedCount} compressed)`)\n lines.push(`✓ [tinyimg] Saved ${formatBytes(summary.bytesSaved)} (original: ${formatBytes(summary.originalSize)} → compressed: ${formatBytes(summary.compressedSize)})`)\n }\n\n return lines\n }\n\n getFileResults(): readonly FileResult[] {\n return this.fileResults\n }\n}\n","import { formatBytes } from '@pz4l/tinyimg-core'\nimport { CompressionStats } from './stats'\n\nexport interface LoggerOptions {\n verbose?: boolean\n strict?: boolean\n}\n\nexport class TinyimgLogger {\n private stats: CompressionStats\n private verbose: boolean\n private strict: boolean\n\n constructor(options: LoggerOptions = {}) {\n this.verbose = options.verbose ?? false\n this.strict = options.strict ?? false\n this.stats = new CompressionStats()\n }\n\n logCompressing(path: string): void {\n if (this.verbose) {\n console.log(`[tinyimg] Compressing ${path}...`)\n }\n }\n\n logCompressed(path: string, originalSize: number, compressedSize: number): void {\n this.stats.recordCompressed(path, originalSize, compressedSize)\n\n if (this.verbose) {\n const saved = ((1 - compressedSize / originalSize) * 100).toFixed(1)\n console.log(`[tinyimg] ✓ Compressed: ${formatBytes(originalSize)} → ${formatBytes(compressedSize)} (${saved}% saved)`)\n }\n }\n\n logCacheHit(path: string, size: number): void {\n this.stats.recordCached(path, size)\n\n if (this.verbose) {\n console.log(`[tinyimg] Cache hit: ${path}`)\n }\n }\n\n logError(path: string, error: string): void {\n this.stats.recordError(path, error)\n\n if (this.strict) {\n // Strict mode (D-13)\n console.error(`[tinyimg] ✖ Failed to compress ${path}: ${error}. Build failed.`)\n }\n else {\n // Non-strict mode (D-12)\n console.warn(`[tinyimg] ⚠ Failed to compress ${path}: ${error}. Using original file.`)\n }\n }\n\n logSummary(): void {\n const lines = this.stats.formatSummary()\n lines.forEach(line => console.log(line))\n }\n\n getStats(): CompressionStats {\n return this.stats\n }\n\n shouldThrowOnError(): boolean {\n return this.strict\n }\n}\n\nexport function createLogger(options: LoggerOptions = {}): TinyimgLogger {\n return new TinyimgLogger(options)\n}\n","import type { FilterOptions } from './filter'\n\nexport interface TinyimgUnpluginOptions extends FilterOptions {\n mode?: 'random' | 'round-robin' | 'priority'\n cache?: boolean\n parallel?: number\n strict?: boolean\n verbose?: boolean\n}\n\nexport interface NormalizedOptions {\n mode: 'random' | 'round-robin' | 'priority'\n cache: boolean\n parallel: number\n strict: boolean\n verbose: boolean\n include?: string[]\n exclude?: string[]\n}\n\nconst VALID_MODES = new Set(['random', 'round-robin', 'priority'])\n\nexport function normalizeOptions(options: TinyimgUnpluginOptions = {}): NormalizedOptions {\n // Validate mode\n if (options.mode !== undefined && !VALID_MODES.has(options.mode)) {\n throw new TypeError(`Invalid mode: \"${options.mode}\". Must be one of: random, round-robin, priority`)\n }\n\n // Validate parallel\n if (options.parallel !== undefined && options.parallel <= 0) {\n throw new RangeError(`Invalid parallel: ${options.parallel}. Must be a positive number`)\n }\n\n return {\n mode: options.mode ?? 'random',\n cache: options.cache ?? true,\n parallel: options.parallel ?? 8,\n strict: options.strict ?? false,\n verbose: options.verbose ?? false,\n include: options.include ? (Array.isArray(options.include) ? options.include : [options.include]) : undefined,\n exclude: options.exclude ? (Array.isArray(options.exclude) ? options.exclude : [options.exclude]) : undefined,\n }\n}\n","import type { TinyimgUnpluginOptions } from './options'\nimport { Buffer } from 'node:buffer'\nimport process from 'node:process'\nimport { compressImage, loadKeys } from '@pz4l/tinyimg-core'\nimport { createUnplugin } from 'unplugin'\nimport { shouldProcessImage } from './filter'\nimport { createLogger } from './logger'\nimport { normalizeOptions } from './options'\n\n// Regex for matching image file extensions\nconst IMAGE_REGEX = /\\.(png|jpg|jpeg|gif|webp|svg)$/i\n\nexport default createUnplugin((options: TinyimgUnpluginOptions = {}): any => {\n // Normalize options\n const normalized = normalizeOptions(options)\n\n // Validate TINYPNG_KEYS (D-15, D-16)\n const keys = loadKeys()\n if (keys.length === 0) {\n throw new Error('TINYPNG_KEYS environment variable is required')\n }\n\n // Create logger\n const logger = createLogger({\n verbose: normalized.verbose,\n strict: normalized.strict,\n })\n\n return {\n name: 'tinyimg-unplugin',\n enforce: 'post', // Run after other transformations (D-02)\n\n async transform(code: any, id: any) {\n // Filter non-image files\n const shouldProcess = shouldProcessImage(id, normalized)\n if (!shouldProcess) {\n return null\n }\n\n // Check production build (D-01)\n const isProd = isProductionBuild(this)\n if (!isProd) {\n return null\n }\n\n // Convert to Buffer\n const buffer = Buffer.from(code)\n\n // Get relative path for logging\n const relativePath = getRelativePath(id)\n\n // Log compression start\n logger.logCompressing(relativePath)\n\n try {\n // Compress image\n const compressed = await compressImage(buffer, {\n projectCacheOnly: true, // Only project cache (D-17)\n cache: normalized.cache,\n mode: normalized.mode as any,\n })\n\n // Log success\n logger.logCompressed(relativePath, buffer.length, compressed.length)\n\n return { code: compressed, map: null }\n }\n catch (error: any) {\n // Log error\n logger.logError(relativePath, error.message)\n\n // Check strict mode\n if (logger.shouldThrowOnError()) {\n throw error\n }\n\n // Non-strict: return null to use original file\n return null\n }\n },\n\n buildEnd() {\n logger.logSummary()\n },\n }\n})\n\n// Helper functions\nfunction isProductionBuild(context: any): boolean {\n // Vite: check config.isBuild (D-01)\n if (context?.config?.isBuild !== undefined) {\n return context.config.isBuild\n }\n\n // Webpack: check mode (D-01)\n if (context?.mode !== undefined) {\n return context.mode === 'production'\n }\n\n // Fallback: check NODE_ENV\n return process.env.NODE_ENV === 'production'\n}\n\nfunction getRelativePath(id: string): string {\n // Convert absolute path to relative for logging\n const root = process.cwd()\n return id.replace(root, '').replace(IMAGE_REGEX, '')\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/filter.ts","../src/options.ts","../src/utils/logger.ts","../src/index.ts"],"sourcesContent":["// @ts-expect-error - micromatch doesn't have types\nimport micromatch from 'micromatch'\nimport path from 'pathe'\n\nexport interface FilterOptions {\n include?: string | string[]\n exclude?: string | string[]\n}\n\nexport const IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg'])\n\nexport function shouldProcessImage(id: string, options: FilterOptions = {}): boolean {\n // Normalize path for cross-platform compatibility (per D-06, D-07)\n const normalizedId = path.normalize(id)\n\n // 1. Check extension first (fast path)\n const ext = path.extname(normalizedId).toLowerCase()\n if (!IMAGE_EXTENSIONS.has(ext)) {\n return false\n }\n\n // 2. Check include pattern if provided\n if (options.include) {\n const includePatterns = Array.isArray(options.include) ? options.include : [options.include]\n const isInclude = micromatch.isMatch(normalizedId, includePatterns)\n if (!isInclude)\n return false\n }\n\n // 3. Check exclude pattern if provided\n if (options.exclude) {\n const excludePatterns = Array.isArray(options.exclude) ? options.exclude : [options.exclude]\n const isExclude = micromatch.isMatch(normalizedId, excludePatterns)\n if (isExclude)\n return false\n }\n\n return true\n}\n","import type { FilterOptions } from './filter'\n\nexport interface TinyimgUnpluginOptions extends FilterOptions {\n mode?: 'random' | 'round-robin' | 'priority'\n cache?: boolean\n parallel?: number\n strict?: boolean\n level?: 'quiet' | 'normal' | 'verbose'\n /** @deprecated use `level: 'verbose'` instead */\n verbose?: boolean\n}\n\nexport interface NormalizedOptions {\n mode: 'random' | 'round-robin' | 'priority'\n cache: boolean\n parallel: number\n strict: boolean\n level: 'quiet' | 'normal' | 'verbose'\n include?: string[]\n exclude?: string[]\n}\n\nconst VALID_MODES = new Set(['random', 'round-robin', 'priority'])\n\nexport function normalizeOptions(options: TinyimgUnpluginOptions = {}): NormalizedOptions {\n // Validate mode\n if (options.mode !== undefined && !VALID_MODES.has(options.mode)) {\n throw new TypeError(`Invalid mode: \"${options.mode}\". Must be one of: random, round-robin, priority`)\n }\n\n // Validate parallel\n if (options.parallel !== undefined && options.parallel <= 0) {\n throw new RangeError(`Invalid parallel: ${options.parallel}. Must be a positive number`)\n }\n\n // 解析日志级别:优先使用 level,兼容旧版 verbose\n const level = options.level ?? (options.verbose === true ? 'verbose' : 'normal')\n\n return {\n mode: options.mode ?? 'random',\n cache: options.cache ?? true,\n parallel: options.parallel ?? 8,\n strict: options.strict ?? false,\n level,\n include: options.include ? (Array.isArray(options.include) ? options.include : [options.include]) : undefined,\n exclude: options.exclude ? (Array.isArray(options.exclude) ? options.exclude : [options.exclude]) : undefined,\n }\n}\n","import { formatBytes } from '@pz4l/tinyimg-core'\nimport kleur from 'kleur'\n\nexport type LogLevel = 'quiet' | 'normal' | 'verbose'\n\n/**\n * 统一的终端日志类(unplugin 专用)\n * 支持 quiet/normal/verbose 三级输出级别\n * 内置统计功能,与 CLI TerminalLogger 对齐\n * 使用统一配色主题(THM-01)\n */\nexport class TerminalLogger {\n private level: LogLevel = 'normal'\n private stats = {\n processed: 0,\n compressed: 0,\n cached: 0,\n errors: 0,\n originalSize: 0,\n compressedSize: 0,\n }\n\n /**\n * 构造函数\n * @param level - 日志级别,默认为 'normal'\n */\n constructor(level: LogLevel = 'normal') {\n this.level = level\n }\n\n /**\n * 设置日志级别\n */\n setLevel(level: LogLevel): void {\n this.level = level\n }\n\n /**\n * 获取当前日志级别\n */\n getLevel(): LogLevel {\n return this.level\n }\n\n /**\n * 记录压缩成功\n * 格式:✓ {path} {originalSize} → {compressedSize} -{percent}%\n */\n successCompress(path: string, originalSize: number, compressedSize: number): void {\n if (this.level === 'quiet') {\n return\n }\n\n // 更新统计\n this.stats.processed++\n this.stats.compressed++\n this.stats.originalSize += originalSize\n this.stats.compressedSize += compressedSize\n\n // 计算节省百分比\n const savedPercent = originalSize === 0 ? 0 : ((1 - compressedSize / originalSize) * 100)\n\n // 输出格式:✓ path original → compressed -percent%\n console.log(\n `${kleur.green('✓')} ${path} ${formatBytes(originalSize)} → ${formatBytes(compressedSize)} -${savedPercent.toFixed(1)}%`,\n )\n }\n\n /**\n * 记录缓存命中\n * 格式:✓ {path} {size} cached\n */\n cacheHit(path: string, size: number): void {\n if (this.level === 'quiet') {\n return\n }\n\n // 更新统计\n this.stats.processed++\n this.stats.cached++\n this.stats.originalSize += size\n this.stats.compressedSize += size\n\n // 输出格式:✓ path size cached\n console.log(`${kleur.green('✓')} ${path} ${formatBytes(size)} cached`)\n }\n\n /**\n * 记录压缩错误\n * strict=true: 使用 console.error 输出 ✗ 前缀(红色)\n * strict=false: 使用 console.warn 输出 ⚠ 前缀(黄色)\n */\n errorCompress(path: string, message: string, strict: boolean): void {\n // 更新统计(错误总是记录)\n this.stats.processed++\n this.stats.errors++\n\n if (strict) {\n // Strict mode: 红色错误前缀\n console.error(`${kleur.red('✗')} ${path} ${message}`)\n }\n else {\n // Non-strict mode: 黄色警告前缀\n console.warn(`${kleur.yellow('⚠')} ${path} ${message}`)\n }\n }\n\n /**\n * 信息日志 - normal/verbose 模式输出\n * 配色:青色 + ℹ 前缀\n */\n info(message: string): void {\n if (this.level === 'quiet') {\n return\n }\n console.log(`${kleur.cyan('ℹ')} ${message}`)\n }\n\n /**\n * 详细日志 - 仅 verbose 模式输出\n * 配色:灰色\n */\n verbose(message: string): void {\n if (this.level === 'verbose') {\n console.log(kleur.gray(message))\n }\n }\n\n /**\n * 输出汇总信息\n * 格式与 CLI compress 汇总一致:\n * ✓ Compression complete\n * Files: N processed, M compressed, K cached\n * [Errors: E](如果有错误)\n * Savings: X.X% avg, Y.Z total\n */\n summary(): void {\n if (this.level === 'quiet' || this.stats.processed === 0) {\n return\n }\n\n const savedBytes = this.stats.originalSize - this.stats.compressedSize\n const savedPercent = this.stats.originalSize === 0\n ? 0\n : (savedBytes / this.stats.originalSize * 100)\n\n // 输出汇总\n console.log(`${kleur.green('✓')} Compression complete`)\n console.log(` Files: ${this.stats.processed} processed, ${this.stats.compressed} compressed, ${this.stats.cached} cached`)\n\n // 如果有错误,显示错误数量\n if (this.stats.errors > 0) {\n console.log(` Errors: ${this.stats.errors}`)\n }\n\n console.log(` Savings: ${savedPercent.toFixed(1)}% avg, ${formatBytes(savedBytes)} total`)\n }\n\n /**\n * 获取当前统计信息\n */\n getStats() {\n return { ...this.stats }\n }\n\n /**\n * 重置统计信息\n */\n resetStats(): void {\n this.stats = {\n processed: 0,\n compressed: 0,\n cached: 0,\n errors: 0,\n originalSize: 0,\n compressedSize: 0,\n }\n }\n}\n\n/**\n * 全局单例 logger 实例\n */\nexport const logger = new TerminalLogger()\n","import type { TinyimgUnpluginOptions } from './options'\nimport { Buffer } from 'node:buffer'\nimport process from 'node:process'\nimport { compressImage, loadKeys } from '@pz4l/tinyimg-core'\nimport path from 'pathe'\nimport { createUnplugin } from 'unplugin'\nimport { shouldProcessImage } from './filter'\nimport { normalizeOptions } from './options'\nimport { TerminalLogger } from './utils/logger'\n\n// Regex for matching image file extensions\nconst IMAGE_REGEX = /\\.(png|jpg|jpeg|gif|webp|svg)$/i\n\nexport default createUnplugin((options: TinyimgUnpluginOptions = {}): any => {\n // Normalize options\n const normalized = normalizeOptions(options)\n\n // Validate TINYPNG_KEYS (D-15, D-16)\n const keys = loadKeys()\n if (keys.length === 0) {\n throw new Error('TINYPNG_KEYS environment variable is required')\n }\n\n // Create logger\n const logger = new TerminalLogger(normalized.level)\n\n return {\n name: 'tinyimg-unplugin',\n enforce: 'post', // Run after other transformations (D-02)\n\n async transform(code: any, id: any) {\n // Filter non-image files\n const shouldProcess = shouldProcessImage(id, normalized)\n if (!shouldProcess) {\n return null\n }\n\n // Check production build (D-01)\n const isProd = isProductionBuild(this)\n if (!isProd) {\n return null\n }\n\n // Convert to Buffer\n const buffer = Buffer.from(code)\n\n // Get relative path for logging\n const relativePath = getRelativePath(id)\n\n try {\n // Compress image\n const { buffer: compressed, meta } = await compressImage(buffer, {\n projectCacheOnly: true, // Only project cache (D-17)\n cache: normalized.cache,\n mode: normalized.mode as any,\n })\n\n // Log based on cache status\n if (meta.cached) {\n logger.cacheHit(relativePath, meta.originalSize)\n }\n else {\n logger.successCompress(relativePath, meta.originalSize, meta.compressedSize)\n // Verbose mode: log compressor name\n if (normalized.level === 'verbose' && meta.compressorName) {\n logger.verbose(` compressor: ${meta.compressorName}`)\n }\n }\n\n return { code: compressed, map: null }\n }\n catch (error: any) {\n // Log error\n logger.errorCompress(relativePath, error.message, normalized.strict)\n\n // Check strict mode\n if (normalized.strict) {\n throw error\n }\n\n // Non-strict: return null to use original file\n return null\n }\n },\n\n buildEnd() {\n logger.summary()\n },\n }\n})\n\n// Helper functions\nfunction isProductionBuild(context: any): boolean {\n // Vite: check config.isBuild (D-01)\n if (context?.config?.isBuild !== undefined) {\n return context.config.isBuild\n }\n\n // Webpack: check mode (D-01)\n if (context?.mode !== undefined) {\n return context.mode === 'production'\n }\n\n // Fallback: check NODE_ENV\n return process.env.NODE_ENV === 'production'\n}\n\nfunction getRelativePath(id: string): string {\n // Convert absolute path to relative for logging using pathe (per D-08)\n const root = process.cwd()\n const relativePath = path.relative(root, id)\n return relativePath.replace(IMAGE_REGEX, '')\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pz4l/tinyimg-unplugin",
3
3
  "type": "module",
4
- "version": "0.3.2",
4
+ "version": "0.3.6",
5
5
  "description": "unplugin for automatic image compression during build (Vite, Webpack, Rolldown)",
6
6
  "author": "pzehrel",
7
7
  "license": "MIT",
@@ -60,9 +60,11 @@
60
60
  }
61
61
  },
62
62
  "dependencies": {
63
+ "kleur": "^4.1.5",
63
64
  "micromatch": "^4.0.8",
65
+ "pathe": "^2.0.3",
64
66
  "unplugin": "^1.0.0",
65
- "@pz4l/tinyimg-core": "0.3.2"
67
+ "@pz4l/tinyimg-core": "0.3.6"
66
68
  },
67
69
  "devDependencies": {
68
70
  "fast-glob": "^3.3.3",