change-image-suffix 2.0.1 → 2.1.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/CHANGELOG.md +30 -0
- package/README.md +2 -5
- package/dist/index.d.ts +2 -0
- package/dist/index.js +65 -158
- package/package.json +8 -4
- package/scripts/postinstall.js +14 -0
- package/scripts/preuninstall.js +14 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [2.1.0](https://github.com/GuoSirius/change-image-suffix/compare/v2.0.2...v2.1.0) (2026-05-21)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* auto context-menu lifecycle hooks, enable declarations, add dev script ([ab44e37](https://github.com/GuoSirius/change-image-suffix/commit/ab44e374040fbfa6715edacb8fb249eeaf746821))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* rewrite bat script, remove gif/heif/jp2, same-format copy, quality 90 ([085a589](https://github.com/GuoSirius/change-image-suffix/commit/085a589f96682626834ac9715f7f228d52bfe89d))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Chores
|
|
19
|
+
|
|
20
|
+
* include .claude/ for cross-device project settings ([c863337](https://github.com/GuoSirius/change-image-suffix/commit/c863337e926826a1799ea0ff4bc6e1f0790afee0))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Documentation
|
|
24
|
+
|
|
25
|
+
* add CLAUDE.md for project onboarding on other devices ([c7f06bc](https://github.com/GuoSirius/change-image-suffix/commit/c7f06bc9c670b31e3e9af9329cbce96b5d3ed065))
|
|
26
|
+
* add project memory for context-menu fix and codebase context ([e8fb432](https://github.com/GuoSirius/change-image-suffix/commit/e8fb43252cf1288d503c24737b84b2f687e19968))
|
|
27
|
+
|
|
28
|
+
### [2.0.2](https://github.com/GuoSirius/change-image-suffix/compare/v2.0.1...v2.0.2) (2026-05-19)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### CI/CD
|
|
32
|
+
|
|
33
|
+
* add build step to GitHub Actions ([d900c7b](https://github.com/GuoSirius/change-image-suffix/commit/d900c7b69c390554aeb1f76a940e178688f8a6c5))
|
|
34
|
+
|
|
5
35
|
### [2.0.1](https://github.com/GuoSirius/change-image-suffix/compare/v2.0.0...v2.0.1) (2026-05-19)
|
|
6
36
|
|
|
7
37
|
## [2.0.0](https://github.com/GuoSirius/change-image-suffix/compare/v1.18.2...v2.0.0) (2026-05-19)
|
package/README.md
CHANGED
|
@@ -68,10 +68,7 @@ cis uninstall-menu
|
|
|
68
68
|
├── 📷 JPG
|
|
69
69
|
├── 🖼 PNG
|
|
70
70
|
├── 📺 AVIF
|
|
71
|
-
|
|
72
|
-
├── 📋 TIFF
|
|
73
|
-
├── 🍎 HEIF
|
|
74
|
-
└── 📐 JPEG2000
|
|
71
|
+
└── 📋 TIFF
|
|
75
72
|
```
|
|
76
73
|
|
|
77
74
|
**适用场景:**
|
|
@@ -152,7 +149,7 @@ cis uninstall-menu
|
|
|
152
149
|
| 类型 | 格式 |
|
|
153
150
|
|------|------|
|
|
154
151
|
| **输入** | png, jpg, jpeg, gif, bmp, tiff, webp, avif |
|
|
155
|
-
| **输出** | webp, jpg, png, avif,
|
|
152
|
+
| **输出** | webp, jpg, png, avif, tiff |
|
|
156
153
|
|
|
157
154
|
---
|
|
158
155
|
|
package/dist/index.d.ts
ADDED
package/dist/index.js
CHANGED
|
@@ -42,7 +42,9 @@ const path = __importStar(require("path"));
|
|
|
42
42
|
const os = __importStar(require("os"));
|
|
43
43
|
const child_process_1 = require("child_process");
|
|
44
44
|
const sharp_1 = __importDefault(require("sharp"));
|
|
45
|
-
//
|
|
45
|
+
// 支持的输入/输出格式
|
|
46
|
+
const SUPPORTED_INPUT_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'tiff', 'tif', 'webp', 'avif'];
|
|
47
|
+
const SUPPORTED_OUTPUT_FORMATS = ['webp', 'jpg', 'jpeg', 'png', 'avif', 'tiff', 'tif'];
|
|
46
48
|
const DEFAULT_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'tiff', 'webp'];
|
|
47
49
|
const DEFAULT_TARGET_FORMAT = 'webp';
|
|
48
50
|
// ─────────────────────────────────────────
|
|
@@ -75,17 +77,14 @@ function regDelete(key) {
|
|
|
75
77
|
}
|
|
76
78
|
function installContextMenu() {
|
|
77
79
|
requireWindows();
|
|
78
|
-
// ── 查找 cis.cmd
|
|
80
|
+
// ── 查找 cis.cmd 路径 ──
|
|
79
81
|
let cisCmd = '';
|
|
80
|
-
let nodeModulesDir = '';
|
|
81
82
|
try {
|
|
82
83
|
cisCmd = (0, child_process_1.execSync)('where cis.cmd', { encoding: 'utf8' }).trim().split('\n')[0].trim();
|
|
83
|
-
nodeModulesDir = path.dirname(cisCmd);
|
|
84
84
|
}
|
|
85
85
|
catch {
|
|
86
86
|
try {
|
|
87
87
|
cisCmd = (0, child_process_1.execSync)('where cis', { encoding: 'utf8' }).trim().split('\n')[0].trim();
|
|
88
|
-
nodeModulesDir = path.dirname(cisCmd);
|
|
89
88
|
}
|
|
90
89
|
catch {
|
|
91
90
|
console.error('❌ 找不到 cis 命令,请先执行 npm link 或 npm install -g change-image-suffix');
|
|
@@ -103,146 +102,60 @@ function installContextMenu() {
|
|
|
103
102
|
fs.copyFileSync(icoSource, icoTarget);
|
|
104
103
|
}
|
|
105
104
|
const iconPath = fs.existsSync(icoTarget) ? icoTarget : cisCmd;
|
|
106
|
-
// ── 辅助脚本路径定义(需要在 batContent 之前,因为 bat 中引用了 ps1Path)──
|
|
107
105
|
const batPath = path.join(appDataDir, 'cis_file.bat');
|
|
108
|
-
|
|
109
|
-
//
|
|
110
|
-
const cisGetfilesContent = `
|
|
111
|
-
Add-Type -AssemblyName Microsoft.VisualBasic
|
|
112
|
-
Add-Type -AssemblyName UIAutomationClient
|
|
113
|
-
$files = @()
|
|
114
|
-
try {
|
|
115
|
-
$shell = New-Object -ComObject Shell.Application
|
|
116
|
-
$windows = $shell.Windows()
|
|
117
|
-
foreach ($win in $windows) {
|
|
118
|
-
if ($win -and $win.FullName -like "*explorer.exe") {
|
|
119
|
-
$selected = $win.Document.SelectedItems()
|
|
120
|
-
foreach ($item in $selected) {
|
|
121
|
-
if ($item -and $item.Path) {
|
|
122
|
-
$files += $item.Path
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
} catch {}
|
|
128
|
-
if ($files.Count -gt 0) {
|
|
129
|
-
$files | ForEach-Object { $_ }
|
|
130
|
-
} else {
|
|
131
|
-
Write-Output "NO_FILES"
|
|
132
|
-
}
|
|
133
|
-
`;
|
|
134
|
-
fs.writeFileSync(ps1Path, cisGetfilesContent, 'utf8');
|
|
135
|
-
// ── bat 脚本:接收 Windows 传递的文件路径和格式参数 ──
|
|
136
|
-
// 根据 Windows ExtendedSubCommandsKey 机制:
|
|
137
|
-
// - 子命令的 command 参数(格式)在前
|
|
138
|
-
// - Windows 自动将父命令收到的文件路径追加在末尾
|
|
139
|
-
// - 最终执行: cmd /c "bat" "格式" "文件路径"
|
|
140
|
-
// 移除 AppliesTo 限制后,bat 需要过滤非图片文件
|
|
106
|
+
// ── bat 脚本:接收格式 + 文件/目录路径,直接调用 cis ──
|
|
107
|
+
// %1 = 格式, %2 %3 ... = Windows 传入的文件/目录路径
|
|
141
108
|
const batContent = `
|
|
142
109
|
@echo off
|
|
143
110
|
chcp 65001 >nul
|
|
144
111
|
setlocal enabledelayedexpansion
|
|
145
112
|
|
|
146
|
-
REM
|
|
147
|
-
set "
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
REM Get cis.cmd path
|
|
151
|
-
for /f "delims=" %%c in ('where cis.cmd 2^>nul') do set "CIS_CMD=%%c"
|
|
152
|
-
|
|
153
|
-
REM Supported image extensions
|
|
154
|
-
set "SUPPORTED_EXT=.png;.jpg;.jpeg;.gif;.bmp;.tiff;.tif;.webp;.avif"
|
|
155
|
-
|
|
156
|
-
REM %1 = format (from subcommand), %2 = file path (from Windows)
|
|
157
|
-
if "%~1"=="" (
|
|
158
|
-
echo Error: No format specified.
|
|
159
|
-
timeout /t 2 >nul
|
|
160
|
-
goto :done
|
|
113
|
+
REM Find cis command in PATH
|
|
114
|
+
set "CIS_CMD="
|
|
115
|
+
for /f "delims=" %%c in ('where cis.cmd 2^>nul') do (
|
|
116
|
+
if "!CIS_CMD!"=="" set "CIS_CMD=%%c"
|
|
161
117
|
)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
set "fileList="
|
|
166
|
-
|
|
167
|
-
REM Windows passes file path as %2. If multiple files selected, use PowerShell.
|
|
168
|
-
REM Otherwise use direct argument.
|
|
169
|
-
if "%~2"=="" (
|
|
170
|
-
REM Multi-file via PowerShell (with retries for reliability)
|
|
171
|
-
for /f "delims=" %%i in ('powershell -ExecutionPolicy Bypass -File "!SCRIPT_DIR!\\cis_getfiles.ps1"') do (
|
|
172
|
-
if not "%%i"=="NO_FILES" (
|
|
173
|
-
call :add_if_image "%%i"
|
|
174
|
-
)
|
|
175
|
-
)
|
|
176
|
-
) else (
|
|
177
|
-
REM Single file or multiple via %2 (space-separated)
|
|
178
|
-
REM Split space-separated paths
|
|
179
|
-
for %%F in (%~2) do (
|
|
180
|
-
call :add_if_image "%%F"
|
|
118
|
+
if "!CIS_CMD!"=="" (
|
|
119
|
+
for /f "delims=" %%c in ('where cis 2^>nul') do (
|
|
120
|
+
if "!CIS_CMD!"=="" set "CIS_CMD=%%c"
|
|
181
121
|
)
|
|
182
122
|
)
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
123
|
+
if "!CIS_CMD!"=="" (
|
|
124
|
+
echo [change-image-suffix] cis command not found. Run: npm install -g change-image-suffix
|
|
125
|
+
pause
|
|
126
|
+
exit /b 1
|
|
187
127
|
)
|
|
188
|
-
goto :done
|
|
189
128
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
129
|
+
if "%~1"=="" (
|
|
130
|
+
echo [change-image-suffix] Error: No format specified.
|
|
131
|
+
pause
|
|
132
|
+
exit /b 1
|
|
133
|
+
)
|
|
195
134
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if "!ext!"=="" exit /b
|
|
135
|
+
set "format=%~1"
|
|
136
|
+
shift
|
|
199
137
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
call set "ext_lower=%%ext_lower:I=i%%
|
|
211
|
-
call set "ext_lower=%%ext_lower:J=j%%
|
|
212
|
-
call set "ext_lower=%%ext_lower:K=k%%
|
|
213
|
-
call set "ext_lower=%%ext_lower:L=l%%
|
|
214
|
-
call set "ext_lower=%%ext_lower:M=m%%
|
|
215
|
-
call set "ext_lower=%%ext_lower:N=n%%
|
|
216
|
-
call set "ext_lower=%%ext_lower:O=o%%
|
|
217
|
-
call set "ext_lower=%%ext_lower:P=p%%
|
|
218
|
-
call set "ext_lower=%%ext_lower:Q=q%%
|
|
219
|
-
call set "ext_lower=%%ext_lower:R=r%%
|
|
220
|
-
call set "ext_lower=%%ext_lower:S=s%%
|
|
221
|
-
call set "ext_lower=%%ext_lower:T=t%%
|
|
222
|
-
call set "ext_lower=%%ext_lower:U=u%%
|
|
223
|
-
call set "ext_lower=%%ext_lower:V=v%%
|
|
224
|
-
call set "ext_lower=%%ext_lower:W=w%%
|
|
225
|
-
call set "ext_lower=%%ext_lower:X=x%%
|
|
226
|
-
call set "ext_lower=%%ext_lower:Y=y%%
|
|
227
|
-
call set "ext_lower=%%ext_lower:Z=z%%"
|
|
138
|
+
set "args="
|
|
139
|
+
:parse
|
|
140
|
+
if "%~1"=="" goto :run
|
|
141
|
+
if exist "%~1\\*" (
|
|
142
|
+
set "args=!args! -p "%~1""
|
|
143
|
+
) else (
|
|
144
|
+
set "args=!args! -f "%~1""
|
|
145
|
+
)
|
|
146
|
+
shift
|
|
147
|
+
goto :parse
|
|
228
148
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
149
|
+
:run
|
|
150
|
+
if "!args!"=="" (
|
|
151
|
+
echo [change-image-suffix] No files or directories to process.
|
|
152
|
+
pause
|
|
153
|
+
exit /b 1
|
|
233
154
|
)
|
|
234
|
-
exit /b
|
|
235
155
|
|
|
236
|
-
|
|
156
|
+
"!CIS_CMD!" -t !format! !args!
|
|
157
|
+
if !errorlevel! neq 0 pause
|
|
237
158
|
endlocal
|
|
238
|
-
exit
|
|
239
|
-
|
|
240
|
-
exit
|
|
241
|
-
|
|
242
|
-
exit
|
|
243
|
-
|
|
244
|
-
exit
|
|
245
|
-
|
|
246
159
|
`;
|
|
247
160
|
fs.writeFileSync(batPath, batContent, 'utf8');
|
|
248
161
|
// ── 格式列表(webp 排第一,其他按常见程度排序)──
|
|
@@ -251,10 +164,7 @@ exit
|
|
|
251
164
|
{ verb: 'jpg', label: '📷 JPG' },
|
|
252
165
|
{ verb: 'png', label: '🖼 PNG' },
|
|
253
166
|
{ verb: 'avif', label: '📺 AVIF' },
|
|
254
|
-
{ verb: 'gif', label: '🎞 GIF' },
|
|
255
167
|
{ verb: 'tiff', label: '📋 TIFF' },
|
|
256
|
-
{ verb: 'heif', label: '🍎 HEIF' },
|
|
257
|
-
{ verb: 'jp2', label: '📐 JPEG2000' },
|
|
258
168
|
];
|
|
259
169
|
// ── 使用 ExtendedSubCommandsKey 方式(PowerShell 7 同款)──
|
|
260
170
|
// 主菜单项配置(每个菜单类型有独立的子菜单路径)
|
|
@@ -331,18 +241,13 @@ function uninstallContextMenu() {
|
|
|
331
241
|
}
|
|
332
242
|
catch { /* ignore */ }
|
|
333
243
|
}
|
|
334
|
-
//
|
|
244
|
+
// 删除批处理文件
|
|
335
245
|
const appDataDir = path.join(os.homedir(), 'AppData', 'Roaming', 'change-image-suffix');
|
|
336
246
|
const batPath = path.join(appDataDir, 'cis_file.bat');
|
|
337
|
-
const ps1Path = path.join(appDataDir, 'cis_getfiles.ps1');
|
|
338
247
|
try {
|
|
339
248
|
fs.unlinkSync(batPath);
|
|
340
249
|
}
|
|
341
250
|
catch { /* ignore */ }
|
|
342
|
-
try {
|
|
343
|
-
fs.unlinkSync(ps1Path);
|
|
344
|
-
}
|
|
345
|
-
catch { /* ignore */ }
|
|
346
251
|
console.log('✅ 右键菜单已卸载');
|
|
347
252
|
}
|
|
348
253
|
// ─────────────────────────────────────────
|
|
@@ -400,7 +305,8 @@ function parseArgs() {
|
|
|
400
305
|
process.exit(0);
|
|
401
306
|
}
|
|
402
307
|
if (arg === '-v' || arg === '--version') {
|
|
403
|
-
|
|
308
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
|
|
309
|
+
console.log(`change-image-suffix v${pkg.version}`);
|
|
404
310
|
process.exit(0);
|
|
405
311
|
}
|
|
406
312
|
if (arg === '-p' || arg === '--path') {
|
|
@@ -488,12 +394,12 @@ function printHelp() {
|
|
|
488
394
|
cis uninstall-menu # 从 Windows 右键菜单移除
|
|
489
395
|
|
|
490
396
|
选项:
|
|
491
|
-
-f, --file <file>
|
|
397
|
+
-f, --file <file> 转换指定文件(可多个,空格分隔)
|
|
492
398
|
-p, --path <dir> 指定工作目录(默认: 当前目录)
|
|
493
399
|
-r, --recursive 递归搜索子目录
|
|
494
400
|
-d, --depth <n> 递归深度限制(需要 -r 选项)
|
|
495
|
-
-e, --extensions <ext>
|
|
496
|
-
-t, --to <format>
|
|
401
|
+
-e, --extensions <ext> 指定源后缀,逗号分隔(不含点号)
|
|
402
|
+
-t, --to <format> 目标格式: webp, jpg, png, avif, tiff(默认: webp)
|
|
497
403
|
-h, --help 显示帮助信息
|
|
498
404
|
-v, --version 显示版本信息
|
|
499
405
|
|
|
@@ -536,9 +442,7 @@ function getOutputPath(inputPath, targetFormat, allInputFiles) {
|
|
|
536
442
|
const dir = path.dirname(inputPath);
|
|
537
443
|
const ext = path.extname(inputPath);
|
|
538
444
|
const basename = path.basename(inputPath, ext);
|
|
539
|
-
const originalExt = ext.slice(1).toLowerCase();
|
|
540
445
|
const targetExt = targetFormat;
|
|
541
|
-
// 源格式与目标格式相同时,直接覆盖
|
|
542
446
|
let coreName = basename;
|
|
543
447
|
const outputDir = path.join(dir, 'output');
|
|
544
448
|
// 检查输入目录中是否有同名(不含扩展名)但不同后缀的文件
|
|
@@ -573,34 +477,39 @@ function getOutputPath(inputPath, targetFormat, allInputFiles) {
|
|
|
573
477
|
async function convertImage(inputPath, targetFormat, allInputFiles) {
|
|
574
478
|
try {
|
|
575
479
|
const outputPath = getOutputPath(inputPath, targetFormat, allInputFiles);
|
|
576
|
-
// 确保 output 目录存在
|
|
577
480
|
const outputDir = path.dirname(outputPath);
|
|
578
481
|
if (!fs.existsSync(outputDir)) {
|
|
579
482
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
580
483
|
}
|
|
484
|
+
const srcExt = path.extname(inputPath).slice(1).toLowerCase();
|
|
485
|
+
const fmt = targetFormat.toLowerCase();
|
|
486
|
+
// 同格式直接复制,避免重新编码导致质量损失
|
|
487
|
+
if (srcExt === fmt || (srcExt === 'jpeg' && fmt === 'jpg') || (srcExt === 'jpg' && fmt === 'jpeg') || (srcExt === 'tif' && fmt === 'tiff') || (srcExt === 'tiff' && fmt === 'tif')) {
|
|
488
|
+
fs.copyFileSync(inputPath, outputPath);
|
|
489
|
+
return { success: true, outputPath };
|
|
490
|
+
}
|
|
491
|
+
if (!SUPPORTED_OUTPUT_FORMATS.includes(fmt)) {
|
|
492
|
+
return { success: false, outputPath: inputPath, error: `不支持的目标格式: ${targetFormat},支持: ${SUPPORTED_OUTPUT_FORMATS.join(', ')}` };
|
|
493
|
+
}
|
|
581
494
|
const image = (0, sharp_1.default)(inputPath);
|
|
582
|
-
switch (
|
|
495
|
+
switch (fmt) {
|
|
583
496
|
case 'webp':
|
|
584
|
-
await image.webp({ quality:
|
|
497
|
+
await image.webp({ quality: 90 }).toFile(outputPath);
|
|
585
498
|
break;
|
|
586
499
|
case 'jpg':
|
|
587
500
|
case 'jpeg':
|
|
588
|
-
await image.jpeg({ quality:
|
|
501
|
+
await image.jpeg({ quality: 90 }).toFile(outputPath);
|
|
589
502
|
break;
|
|
590
503
|
case 'png':
|
|
591
|
-
await image.png({
|
|
592
|
-
break;
|
|
593
|
-
case 'gif':
|
|
594
|
-
await image.gif().toFile(outputPath);
|
|
504
|
+
await image.png({ compressionLevel: 6 }).toFile(outputPath);
|
|
595
505
|
break;
|
|
596
506
|
case 'tiff':
|
|
597
507
|
case 'tif':
|
|
598
|
-
await image.tiff({ quality:
|
|
508
|
+
await image.tiff({ quality: 90 }).toFile(outputPath);
|
|
599
509
|
break;
|
|
600
510
|
case 'avif':
|
|
601
|
-
await image.avif({ quality:
|
|
511
|
+
await image.avif({ quality: 90 }).toFile(outputPath);
|
|
602
512
|
break;
|
|
603
|
-
default: await image.toFormat(targetFormat).toFile(outputPath);
|
|
604
513
|
}
|
|
605
514
|
return { success: true, outputPath };
|
|
606
515
|
}
|
|
@@ -634,12 +543,11 @@ async function main() {
|
|
|
634
543
|
console.log(`🎯 目标格式: ${options.targetFormat}`);
|
|
635
544
|
console.log(`📦 待处理: ${files.length} 个文件\n`);
|
|
636
545
|
console.log('----------------------------------------\n');
|
|
637
|
-
const supportedExts = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'tiff', 'tif', 'webp', 'avif'];
|
|
638
546
|
let totalSuccess = 0;
|
|
639
547
|
let totalFail = 0;
|
|
640
548
|
for (const filePath of files) {
|
|
641
549
|
const ext = path.extname(filePath).slice(1).toLowerCase();
|
|
642
|
-
if (!
|
|
550
|
+
if (!SUPPORTED_INPUT_EXTENSIONS.includes(ext)) {
|
|
643
551
|
console.log(` ⚠️ 跳过(不支持格式): ${filePath}`);
|
|
644
552
|
totalFail++;
|
|
645
553
|
continue;
|
|
@@ -674,8 +582,7 @@ async function main() {
|
|
|
674
582
|
}
|
|
675
583
|
if (stat.isFile()) {
|
|
676
584
|
const ext = path.extname(inputPath).slice(1).toLowerCase();
|
|
677
|
-
|
|
678
|
-
if (!supportedExts.includes(ext)) {
|
|
585
|
+
if (!SUPPORTED_INPUT_EXTENSIONS.includes(ext)) {
|
|
679
586
|
console.log(` ⚠️ 跳过(不支持格式): ${inputPath}`);
|
|
680
587
|
totalFail++;
|
|
681
588
|
continue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "change-image-suffix",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "批量转换图片格式的CLI工具,支持递归搜索、深度限制、指定后缀、Windows右键菜单等功能",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"files": [
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
"assets/",
|
|
9
9
|
"README.md",
|
|
10
10
|
"LICENSE",
|
|
11
|
-
"CHANGELOG.md"
|
|
11
|
+
"CHANGELOG.md",
|
|
12
|
+
"scripts/postinstall.js",
|
|
13
|
+
"scripts/preuninstall.js"
|
|
12
14
|
],
|
|
13
15
|
"bin": {
|
|
14
16
|
"change-image-suffix": "./dist/index.js",
|
|
@@ -21,13 +23,15 @@
|
|
|
21
23
|
"scripts": {
|
|
22
24
|
"clean": "rimraf dist",
|
|
23
25
|
"build": "tsc",
|
|
26
|
+
"dev": "tsc --watch",
|
|
27
|
+
"postinstall": "node scripts/postinstall.js",
|
|
28
|
+
"preuninstall": "node scripts/preuninstall.js",
|
|
24
29
|
"prepublishOnly": "npm run clean && npm run build",
|
|
25
30
|
"release": "node scripts/release.js",
|
|
26
31
|
"release:patch": "node scripts/release.js --patch",
|
|
27
32
|
"release:minor": "node scripts/release.js --minor",
|
|
28
33
|
"release:major": "node scripts/release.js --major",
|
|
29
|
-
"lint": "
|
|
30
|
-
"lint:fix": "echo \"Linting fix not configured yet\"",
|
|
34
|
+
"lint": "tsc --noEmit",
|
|
31
35
|
"typecheck": "tsc --noEmit"
|
|
32
36
|
},
|
|
33
37
|
"keywords": [
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// npm lifecycle hook: runs after npm install/update
|
|
2
|
+
// Auto-registers Windows context menu on global install
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const { execSync } = require('child_process');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
if (os.platform() === 'win32' && process.env.npm_config_global === 'true') {
|
|
8
|
+
try {
|
|
9
|
+
const indexJs = path.join(__dirname, '..', 'dist', 'index.js');
|
|
10
|
+
execSync(`node "${indexJs}" install-menu`, { stdio: 'inherit' });
|
|
11
|
+
} catch (e) {
|
|
12
|
+
console.warn('⚠️ Context menu auto-register failed. Run "cis install-menu" manually.');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// npm lifecycle hook: runs before npm uninstall
|
|
2
|
+
// Cleans up Windows context menu to avoid leftover registry entries
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const { execSync } = require('child_process');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
if (os.platform() === 'win32') {
|
|
8
|
+
try {
|
|
9
|
+
const indexJs = path.join(__dirname, '..', 'dist', 'index.js');
|
|
10
|
+
execSync(`node "${indexJs}" uninstall-menu`, { stdio: 'inherit' });
|
|
11
|
+
} catch (e) {
|
|
12
|
+
// Ignore cleanup errors — the menu will be orphaned but harmless
|
|
13
|
+
}
|
|
14
|
+
}
|