juice-email-cli 2.3.3 → 2.3.4
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/juice.js +2 -0
- package/package.json +1 -1
- package/src/context-menu.js +72 -49
- package/src/init.js +103 -3
- package/src/view.js +44 -23
package/bin/juice.js
CHANGED
|
@@ -103,6 +103,7 @@ program
|
|
|
103
103
|
.option('-t, --template <file-path>', '仅拷贝模板 HTML 文件')
|
|
104
104
|
.option('-s, --snippet <file-path>', '仅拷贝片段 HTML 文件')
|
|
105
105
|
.option('-c, --config <file-path>', '仅拷贝配置 YAML 文件')
|
|
106
|
+
.option('--all [target]', '拷贝整个 EDM 资源目录到当前或指定目录')
|
|
106
107
|
.action(async (initPath, options) => {
|
|
107
108
|
const { runInitMode } = require('../src/init');
|
|
108
109
|
await runInitMode({
|
|
@@ -110,6 +111,7 @@ program
|
|
|
110
111
|
template: options.template || null,
|
|
111
112
|
snippet: options.snippet || null,
|
|
112
113
|
config: options.config || null,
|
|
114
|
+
all: options.all !== undefined ? (options.all || true) : null,
|
|
113
115
|
});
|
|
114
116
|
});
|
|
115
117
|
|
package/package.json
CHANGED
package/src/context-menu.js
CHANGED
|
@@ -262,54 +262,60 @@ function wrapWithPause(nodePath, scriptPath, cliArgs) {
|
|
|
262
262
|
// ─── Directory / Background 菜单注册 ──────────────────────────────────────────
|
|
263
263
|
|
|
264
264
|
/**
|
|
265
|
-
* Register
|
|
266
|
-
*
|
|
267
|
-
* so we register each item as a standalone shell verb.
|
|
265
|
+
* Register Directory and Background right-click menus using SubCommands
|
|
266
|
+
* + CommandStore (same format as file-type menus for consistent UX).
|
|
268
267
|
*/
|
|
269
268
|
function registerDirBgMenus(nodePath, scriptPath, iconPath, pwshPath) {
|
|
269
|
+
const node = nodePath.replace(/'/g, "''");
|
|
270
|
+
const script = scriptPath.replace(/'/g, "''");
|
|
271
|
+
|
|
272
|
+
// CommandStore subcommand names
|
|
273
|
+
const DIR_VIEW = 'JuiceEmailDir.View';
|
|
274
|
+
const DIR_COPYALL = 'JuiceEmailDir.CopyAll';
|
|
275
|
+
const DIR_COPYPICK= 'JuiceEmailDir.CopyPick';
|
|
276
|
+
const DIR_PWSH = 'JuiceEmailDir.Pwsh';
|
|
277
|
+
|
|
278
|
+
// Helper: register a CommandStore entry
|
|
279
|
+
function regCmd(name, label, psCmd) {
|
|
280
|
+
const cmdKey = `${HKCU_SHELL}\\${name}`;
|
|
281
|
+
regAdd(cmdKey, 'MUIVerb', 'REG_SZ', label);
|
|
282
|
+
regAdd(cmdKey, 'Icon', 'REG_SZ', iconPath);
|
|
283
|
+
// Always pause so user can see output
|
|
284
|
+
const fullPs = `${psCmd}; Write-Host ''; Read-Host 'Press Enter to close'`;
|
|
285
|
+
regAdd(`${cmdKey}\\command`, '', 'REG_SZ', `powershell.exe -Command "${fullPs}"`);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// View: non-interactive, no directory dependency
|
|
289
|
+
regCmd(DIR_VIEW, '📋 查看可用资源', `& '${node}' '${script}' view`);
|
|
290
|
+
|
|
291
|
+
// CopyAll: copy entire EDM to clicked directory
|
|
292
|
+
regCmd(DIR_COPYALL, '📦 拷贝全部资源', `& '${node}' '${script}' init --all '%V'`);
|
|
293
|
+
|
|
294
|
+
// CopyPick: interactive, cd to directory first so output lands there
|
|
295
|
+
regCmd(DIR_COPYPICK, '📥 选择资源拷贝', `Set-Location '%V'; & '${node}' '${script}' init`);
|
|
296
|
+
|
|
297
|
+
// Pwsh
|
|
298
|
+
if (pwshPath) {
|
|
299
|
+
const pwshKey = `${HKCU_SHELL}\\${DIR_PWSH}`;
|
|
300
|
+
regAdd(pwshKey, 'MUIVerb', 'REG_SZ', '📂 在此打开终端');
|
|
301
|
+
regAdd(pwshKey, 'Icon', 'REG_SZ', pwshPath);
|
|
302
|
+
regAdd(`${pwshKey}\\command`, '', 'REG_SZ',
|
|
303
|
+
`"${pwshPath}" -NoExit -Command "Set-Location -LiteralPath '%V'"`);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Build SubCommands string
|
|
307
|
+
const subCmds = [DIR_VIEW, DIR_COPYALL, DIR_COPYPICK];
|
|
308
|
+
if (pwshPath) subCmds.push(DIR_PWSH);
|
|
309
|
+
|
|
310
|
+
// Parent keys: Directory + Directory\Background
|
|
270
311
|
const roots = [
|
|
271
312
|
`${HKCU_SHELL}\\Directory\\shell`,
|
|
272
313
|
`${HKCU_SHELL}\\Directory\\Background\\shell`,
|
|
273
314
|
];
|
|
274
|
-
|
|
275
|
-
// First, clean up old keys from all previous versions to prevent stale entries
|
|
276
315
|
for (const root of roots) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
regDelete(`${root}\\JuiceEmail.Init`);
|
|
281
|
-
regDelete(`${root}\\JuiceEmail.View`);
|
|
282
|
-
regDelete(`${root}\\JuiceEmail.Browse`);
|
|
283
|
-
regDelete(`${root}\\JuiceEmail.Pwsh`);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
for (const root of roots) {
|
|
287
|
-
// 📥 从资源库拷贝到此处
|
|
288
|
-
const initKey = `${root}\\JuiceEmail.Init`;
|
|
289
|
-
regAdd(initKey, 'MUIVerb', 'REG_SZ', '📥 从资源库拷贝到此处');
|
|
290
|
-
regAdd(initKey, 'Icon', 'REG_SZ', iconPath);
|
|
291
|
-
regAdd(`${initKey}\\command`, '', 'REG_SZ', wrapInteractive(nodePath, scriptPath, 'init'));
|
|
292
|
-
|
|
293
|
-
// 📦 查看资源列表
|
|
294
|
-
const viewKey = `${root}\\JuiceEmail.View`;
|
|
295
|
-
regAdd(viewKey, 'MUIVerb', 'REG_SZ', '📦 查看资源列表');
|
|
296
|
-
regAdd(viewKey, 'Icon', 'REG_SZ', iconPath);
|
|
297
|
-
regAdd(`${viewKey}\\command`, '', 'REG_SZ', wrapWithPause(nodePath, scriptPath, 'view'));
|
|
298
|
-
|
|
299
|
-
// 📋 浏览资源库
|
|
300
|
-
const browseKey = `${root}\\JuiceEmail.Browse`;
|
|
301
|
-
regAdd(browseKey, 'MUIVerb', 'REG_SZ', '📋 浏览资源库');
|
|
302
|
-
regAdd(browseKey, 'Icon', 'REG_SZ', iconPath);
|
|
303
|
-
regAdd(`${browseKey}\\command`, '', 'REG_SZ', wrapInteractive(nodePath, scriptPath, 'view -i'));
|
|
304
|
-
|
|
305
|
-
// 📂 在此打开终端
|
|
306
|
-
if (pwshPath) {
|
|
307
|
-
const pwshKey = `${root}\\JuiceEmail.Pwsh`;
|
|
308
|
-
regAdd(pwshKey, 'MUIVerb', 'REG_SZ', '📂 在此打开终端');
|
|
309
|
-
regAdd(pwshKey, 'Icon', 'REG_SZ', pwshPath);
|
|
310
|
-
regAdd(`${pwshKey}\\command`, '', 'REG_SZ',
|
|
311
|
-
`"${pwshPath}" -NoExit -Command "Set-Location -LiteralPath '%V'"`);
|
|
312
|
-
}
|
|
316
|
+
regAdd(`${root}\\${PARENT_KEY_NAME}`, 'MUIVerb', 'REG_SZ', '📧 用 juice 生成邮件 HTML');
|
|
317
|
+
regAdd(`${root}\\${PARENT_KEY_NAME}`, 'Icon', 'REG_SZ', iconPath);
|
|
318
|
+
regAdd(`${root}\\${PARENT_KEY_NAME}`, 'SubCommands', 'REG_SZ', subCmds.join(';'));
|
|
313
319
|
}
|
|
314
320
|
}
|
|
315
321
|
|
|
@@ -367,8 +373,20 @@ async function registerContextMenu() {
|
|
|
367
373
|
regAdd(parentKey, 'ExtendedSubCommandsKey', 'REG_SZ', SUB_CMDS_CONTAINER_YAML);
|
|
368
374
|
}
|
|
369
375
|
|
|
370
|
-
// Directory / Background →
|
|
376
|
+
// Directory / Background → 子菜单(SubCommands + CommandStore)
|
|
371
377
|
console.log(chalk.gray(' 注册文件夹/空白处菜单...'));
|
|
378
|
+
// Clean up old individual entries from v2.3.x before registering new format
|
|
379
|
+
for (const root of [`${HKCU_SHELL}\\Directory\\shell`, `${HKCU_SHELL}\\Directory\\Background\\shell`]) {
|
|
380
|
+
regDelete(`${root}\\${PARENT_KEY_NAME}`);
|
|
381
|
+
regDelete(`${root}\\JuiceEmail.Init`);
|
|
382
|
+
regDelete(`${root}\\JuiceEmail.View`);
|
|
383
|
+
regDelete(`${root}\\JuiceEmail.Browse`);
|
|
384
|
+
regDelete(`${root}\\JuiceEmail.Pwsh`);
|
|
385
|
+
}
|
|
386
|
+
// Clean up old CommandStore entries for Dir menu (from any prior version)
|
|
387
|
+
for (const name of ['JuiceEmailDir.View', 'JuiceEmailDir.CopyAll', 'JuiceEmailDir.CopyPick', 'JuiceEmailDir.Pwsh']) {
|
|
388
|
+
regDelete(`${HKCU_SHELL}\\${name}`);
|
|
389
|
+
}
|
|
372
390
|
registerDirBgMenus(nodePath, scriptPath, iconPath, pwshPath);
|
|
373
391
|
|
|
374
392
|
if (!ok) {
|
|
@@ -392,10 +410,11 @@ async function registerContextMenu() {
|
|
|
392
410
|
` ├── 📋 浏览资源库 → juice view -i\n` +
|
|
393
411
|
(pwshPath ? ` └── 📂 打开 PowerShell\n` : '') +
|
|
394
412
|
`\n ${chalk.bold('文件夹 / 空白处')} 右键:\n` +
|
|
395
|
-
`
|
|
396
|
-
`
|
|
397
|
-
`
|
|
398
|
-
|
|
413
|
+
` ${chalk.bold('📧 用 juice 生成邮件 HTML')}\n` +
|
|
414
|
+
` ├── 📋 查看可用资源 → juice view\n` +
|
|
415
|
+
` ├── 📦 拷贝全部资源 → juice init --all\n` +
|
|
416
|
+
` ├── 📥 选择资源拷贝 → juice init\n` +
|
|
417
|
+
(pwshPath ? ` └── 📂 在此打开终端\n` : '') +
|
|
399
418
|
'\n' +
|
|
400
419
|
chalk.gray(' 注意:如菜单未出现,请重启文件资源管理器(explorer.exe)。\n')
|
|
401
420
|
);
|
|
@@ -422,18 +441,22 @@ async function unregisterContextMenu() {
|
|
|
422
441
|
if (regDelete(`${root}\\${PARENT_KEY_NAME}`)) removed++;
|
|
423
442
|
}
|
|
424
443
|
|
|
425
|
-
// 清理 Directory / Background
|
|
444
|
+
// 清理 Directory / Background 父菜单 + 旧版独立条目
|
|
426
445
|
const dirBgRoots = [
|
|
427
446
|
`${HKCU_SHELL}\\Directory\\shell`,
|
|
428
447
|
`${HKCU_SHELL}\\Directory\\Background\\shell`,
|
|
429
448
|
];
|
|
430
|
-
|
|
431
|
-
|
|
449
|
+
// New parent key + old individual entries from v2.3.x
|
|
450
|
+
const dirBgKeys = [PARENT_KEY_NAME, 'JuiceEmail.Init', 'JuiceEmail.View', 'JuiceEmail.Browse', 'JuiceEmail.Pwsh'];
|
|
432
451
|
for (const root of dirBgRoots) {
|
|
433
452
|
for (const key of dirBgKeys) {
|
|
434
453
|
if (regDelete(`${root}\\${key}`)) removed++;
|
|
435
454
|
}
|
|
436
455
|
}
|
|
456
|
+
// CommandStore entries for Dir menus
|
|
457
|
+
for (const name of ['JuiceEmailDir.View', 'JuiceEmailDir.CopyAll', 'JuiceEmailDir.CopyPick', 'JuiceEmailDir.Pwsh']) {
|
|
458
|
+
if (regDelete(`${HKCU_SHELL}\\${name}`)) removed++;
|
|
459
|
+
}
|
|
437
460
|
|
|
438
461
|
// 清理所有子命令容器(含旧版 Dir/Bg 容器)
|
|
439
462
|
if (regDelete(`${HKCU_SHELL}\\${SUB_CMDS_CONTAINER_HTML}`)) removed++;
|
package/src/init.js
CHANGED
|
@@ -71,8 +71,7 @@ function deriveDefaultName(filePath) {
|
|
|
71
71
|
async function directCopy(srcPath, cwd) {
|
|
72
72
|
const resolved = path.resolve(srcPath);
|
|
73
73
|
if (!fs.existsSync(resolved)) {
|
|
74
|
-
|
|
75
|
-
process.exit(1);
|
|
74
|
+
throw new Error(`文件不存在:${resolved}`);
|
|
76
75
|
}
|
|
77
76
|
|
|
78
77
|
const destName = await promptOutputName(
|
|
@@ -231,9 +230,110 @@ async function interactiveInit(edmDir, cwd) {
|
|
|
231
230
|
|
|
232
231
|
// ─── Main Entry ──────────────────────────────────────────────────────────────
|
|
233
232
|
|
|
234
|
-
|
|
233
|
+
function copyDir(src, dest) {
|
|
234
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
235
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
236
|
+
const srcPath = path.join(src, entry.name);
|
|
237
|
+
const destPath = path.join(dest, entry.name);
|
|
238
|
+
if (entry.isDirectory()) {
|
|
239
|
+
copyDir(srcPath, destPath);
|
|
240
|
+
} else {
|
|
241
|
+
fs.copyFileSync(srcPath, destPath);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function dirSize(dir) {
|
|
247
|
+
let size = 0;
|
|
248
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
249
|
+
const p = path.join(dir, entry.name);
|
|
250
|
+
if (entry.isDirectory()) {
|
|
251
|
+
size += dirSize(p);
|
|
252
|
+
} else {
|
|
253
|
+
size += fs.statSync(p).size;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return size;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function countFiles(dir) {
|
|
260
|
+
let count = 0;
|
|
261
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
262
|
+
if (entry.isDirectory()) {
|
|
263
|
+
count += countFiles(path.join(dir, entry.name));
|
|
264
|
+
} else {
|
|
265
|
+
count++;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return count;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function findNextEdmVersion(targetDir) {
|
|
272
|
+
let v = 1;
|
|
273
|
+
while (true) {
|
|
274
|
+
const candidate = path.join(targetDir, 'edm-v' + v);
|
|
275
|
+
if (!fs.existsSync(candidate)) return candidate;
|
|
276
|
+
v++;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async function runInitMode({ initPath, template, snippet, config, all }) {
|
|
235
281
|
const cwd = process.cwd();
|
|
236
282
|
|
|
283
|
+
// --all: copy entire EDM directory
|
|
284
|
+
if (all) {
|
|
285
|
+
let edmDir;
|
|
286
|
+
try {
|
|
287
|
+
edmDir = resolveEdmDir();
|
|
288
|
+
} catch (err) {
|
|
289
|
+
console.error(chalk.red(`\n ✘ ${err.message}\n`));
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
const target = all === true ? cwd : path.resolve(all);
|
|
293
|
+
const destDir = path.join(target, 'edm');
|
|
294
|
+
|
|
295
|
+
// Prevent deleting the source when target overlaps
|
|
296
|
+
if (path.resolve(edmDir) === path.resolve(destDir)) {
|
|
297
|
+
console.log(chalk.yellow('目标目录与 EDM 资源库相同,无需拷贝。\n'));
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
console.log(chalk.cyan(`\n 拷贝 EDM 资源库...`));
|
|
301
|
+
console.log(chalk.gray(` 源:${edmDir}`));
|
|
302
|
+
console.log(chalk.gray(` 目标:${destDir}`));
|
|
303
|
+
|
|
304
|
+
if (fs.existsSync(destDir)) {
|
|
305
|
+
const { select } = await import('@inquirer/prompts');
|
|
306
|
+
const vName = findNextEdmVersion(target);
|
|
307
|
+
const action = await select({
|
|
308
|
+
message: `目录 ${destDir} 已存在:`,
|
|
309
|
+
choices: [
|
|
310
|
+
{ name: '覆盖', value: 'overwrite' },
|
|
311
|
+
{ name: `自动版本号(${path.basename(vName)})`, value: 'version' },
|
|
312
|
+
{ name: '取消', value: 'cancel' },
|
|
313
|
+
],
|
|
314
|
+
});
|
|
315
|
+
if (action === 'cancel') {
|
|
316
|
+
console.log(chalk.gray('已取消。\n'));
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (action === 'version') {
|
|
320
|
+
copyDir(edmDir, vName);
|
|
321
|
+
const edmSize = dirSize(vName);
|
|
322
|
+
const fileCount = countFiles(vName);
|
|
323
|
+
console.log(chalk.green(`\n✔ 已拷贝 EDM 资源库 → ${path.basename(vName)}(${fileCount} 个文件,${fmtBytes(edmSize)})\n`));
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
// overwrite: remove and replace
|
|
327
|
+
fs.rmSync(destDir, { recursive: true, force: true });
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
copyDir(edmDir, destDir);
|
|
331
|
+
const edmSize = dirSize(destDir);
|
|
332
|
+
const fileCount = countFiles(destDir);
|
|
333
|
+
console.log(chalk.green(`\n✔ 已拷贝 EDM 资源库(${fileCount} 个文件,${fmtBytes(edmSize)})\n`));
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
237
337
|
// Direct copy modes
|
|
238
338
|
if (template) {
|
|
239
339
|
await directCopy(template, cwd);
|
package/src/view.js
CHANGED
|
@@ -12,6 +12,27 @@ function fmtBytes(b) {
|
|
|
12
12
|
return b < 1024 ? `${b} B` : `${(b / 1024).toFixed(1)} KB`;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Copy src to dest, auto-versioning (-v1, -v2, ...) if dest exists.
|
|
17
|
+
* Returns the actual destination path used.
|
|
18
|
+
*/
|
|
19
|
+
function copyFileSafe(srcPath, destPath) {
|
|
20
|
+
if (!fs.existsSync(destPath)) {
|
|
21
|
+
fs.copyFileSync(srcPath, destPath);
|
|
22
|
+
return destPath;
|
|
23
|
+
}
|
|
24
|
+
const parsed = path.parse(destPath);
|
|
25
|
+
let v = 1;
|
|
26
|
+
while (true) {
|
|
27
|
+
const alt = path.join(parsed.dir, parsed.name + '-v' + v + parsed.ext);
|
|
28
|
+
if (!fs.existsSync(alt)) {
|
|
29
|
+
fs.copyFileSync(srcPath, alt);
|
|
30
|
+
return alt;
|
|
31
|
+
}
|
|
32
|
+
v++;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
15
36
|
// ─── Path Parser ──────────────────────────────────────────────────────────────
|
|
16
37
|
|
|
17
38
|
/**
|
|
@@ -327,13 +348,17 @@ async function showCheckbox(title, choices) {
|
|
|
327
348
|
* Trigger copy via the init module.
|
|
328
349
|
*/
|
|
329
350
|
async function copyResource(type, resourcePath, cwd) {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
351
|
+
try {
|
|
352
|
+
const { runInitMode } = require('./init');
|
|
353
|
+
if (type === 'template') {
|
|
354
|
+
await runInitMode({ template: resourcePath });
|
|
355
|
+
} else if (type === 'snippet') {
|
|
356
|
+
await runInitMode({ snippet: resourcePath });
|
|
357
|
+
} else if (type === 'config') {
|
|
358
|
+
await runInitMode({ config: resourcePath });
|
|
359
|
+
}
|
|
360
|
+
} catch (err) {
|
|
361
|
+
console.error(chalk.red(` ✘ 拷贝失败:${err.message}`));
|
|
337
362
|
}
|
|
338
363
|
}
|
|
339
364
|
|
|
@@ -551,7 +576,7 @@ async function interactiveBrowse(edmDir, startParsed) {
|
|
|
551
576
|
if (versions.length > 0) {
|
|
552
577
|
const tplPath = versions[0].templatePath;
|
|
553
578
|
copyItems.push({
|
|
554
|
-
name:
|
|
579
|
+
name: `📋 模板 HTML(${versions[0].meta.name || versions[0].name})`,
|
|
555
580
|
value: 'template',
|
|
556
581
|
description: path.basename(tplPath),
|
|
557
582
|
checked: true,
|
|
@@ -588,7 +613,9 @@ async function interactiveBrowse(edmDir, startParsed) {
|
|
|
588
613
|
});
|
|
589
614
|
|
|
590
615
|
if (selected.length > 0) {
|
|
591
|
-
|
|
616
|
+
// Include template version in default name for clarity
|
|
617
|
+
const versionName = versions.length > 0 ? versions[0].name : 'template';
|
|
618
|
+
const defaultBaseName = `${current.brand}-${versionName}-${current.series}-${current.variant}`;
|
|
592
619
|
const { promptOutputName: pn } = require('./snippet');
|
|
593
620
|
const outputBaseName = await pn(defaultBaseName, process.cwd());
|
|
594
621
|
const cwd = process.cwd();
|
|
@@ -596,28 +623,22 @@ async function interactiveBrowse(edmDir, startParsed) {
|
|
|
596
623
|
|
|
597
624
|
console.log(chalk.green('\n✔ 已拷贝:'));
|
|
598
625
|
if (selected.includes('template') && versions.length > 0) {
|
|
599
|
-
const
|
|
600
|
-
const dest = path.join(cwd, outputBaseName + path.extname(
|
|
601
|
-
|
|
602
|
-
console.log(` ${chalk.cyan('·')} ${cwdRel(
|
|
626
|
+
const src = versions[0].templatePath;
|
|
627
|
+
const dest = path.join(cwd, outputBaseName + path.extname(src));
|
|
628
|
+
const actual = copyFileSafe(src, dest);
|
|
629
|
+
console.log(` ${chalk.cyan('·')} ${cwdRel(actual)} ${chalk.gray('(模板, ' + fmtBytes(fs.statSync(actual).size) + ')')}`);
|
|
603
630
|
}
|
|
604
631
|
if (selected.includes('snippet') && fs.existsSync(snipPath)) {
|
|
605
632
|
const dest = path.join(cwd, outputBaseName + '-snippet' + path.extname(snipPath));
|
|
606
|
-
|
|
607
|
-
console.log(` ${chalk.cyan('·')} ${cwdRel(
|
|
633
|
+
const actual = copyFileSafe(snipPath, dest);
|
|
634
|
+
console.log(` ${chalk.cyan('·')} ${cwdRel(actual)} ${chalk.gray('(片段, ' + fmtBytes(fs.statSync(actual).size) + ')')}`);
|
|
608
635
|
}
|
|
609
636
|
if (selected.includes('config') && configs.length > 0) {
|
|
610
637
|
const optimal = configs.find(c => c.isOptimal);
|
|
611
638
|
const cfgPath = optimal ? optimal.path : configs[0].path;
|
|
612
639
|
const dest = path.join(cwd, 'juice.yaml');
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
fs.copyFileSync(cfgPath, alt);
|
|
616
|
-
console.log(` ${chalk.cyan('·')} ${cwdRel(alt)} ${chalk.gray('(配置, ' + fmtBytes(fs.statSync(alt).size) + ')')}`);
|
|
617
|
-
} else {
|
|
618
|
-
fs.copyFileSync(cfgPath, dest);
|
|
619
|
-
console.log(` ${chalk.cyan('·')} ${cwdRel(dest)} ${chalk.gray('(配置, ' + fmtBytes(fs.statSync(dest).size) + ')')}`);
|
|
620
|
-
}
|
|
640
|
+
const actual = copyFileSafe(cfgPath, dest);
|
|
641
|
+
console.log(` ${chalk.cyan('·')} ${cwdRel(actual)} ${chalk.gray('(配置, ' + fmtBytes(fs.statSync(actual).size) + ')')}`);
|
|
621
642
|
}
|
|
622
643
|
console.log();
|
|
623
644
|
}
|