juice-email-cli 2.3.6 → 2.3.8
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 +29 -6
- package/package.json +1 -1
- package/src/init.js +13 -11
package/bin/juice.js
CHANGED
|
@@ -5,6 +5,29 @@ const { program } = require('commander');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const pkg = require('../package.json');
|
|
7
7
|
const { run } = require('../src/index');
|
|
8
|
+
let ExitPromptError;
|
|
9
|
+
try {
|
|
10
|
+
({ ExitPromptError } = require('@inquirer/core'));
|
|
11
|
+
} catch (_) {}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Wrap an async action handler to catch Ctrl+C and other errors gracefully.
|
|
15
|
+
*/
|
|
16
|
+
function safeAction(fn) {
|
|
17
|
+
return async (...args) => {
|
|
18
|
+
try {
|
|
19
|
+
await fn(...args);
|
|
20
|
+
} catch (err) {
|
|
21
|
+
if (ExitPromptError && err instanceof ExitPromptError) {
|
|
22
|
+
// Ctrl+C — exit silently
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
console.error('\n' + require('chalk').red(` ✘ ${err.message}`));
|
|
26
|
+
if (process.env.DEBUG) console.error(err);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
8
31
|
|
|
9
32
|
program
|
|
10
33
|
.name('juice')
|
|
@@ -87,7 +110,7 @@ program
|
|
|
87
110
|
.option('--templates', '列出所有模板')
|
|
88
111
|
.option('--series', '列出所有系列')
|
|
89
112
|
.option('--snippets', '列出所有片段')
|
|
90
|
-
.action(async (viewPath, options) => {
|
|
113
|
+
.action(safeAction(async (viewPath, options) => {
|
|
91
114
|
const { runViewMode } = require('../src/view');
|
|
92
115
|
await runViewMode({
|
|
93
116
|
viewPath: viewPath || null,
|
|
@@ -96,7 +119,7 @@ program
|
|
|
96
119
|
: (options.series ? 'series'
|
|
97
120
|
: (options.snippets ? 'snippets' : null)),
|
|
98
121
|
});
|
|
99
|
-
});
|
|
122
|
+
}));
|
|
100
123
|
|
|
101
124
|
// ─── Subcommand: juice init ─────────────────────────────────────────────
|
|
102
125
|
program
|
|
@@ -106,7 +129,7 @@ program
|
|
|
106
129
|
.option('-s, --snippet <file-path>', '仅拷贝片段 HTML 文件')
|
|
107
130
|
.option('-c, --config <file-path>', '仅拷贝配置 YAML 文件')
|
|
108
131
|
.option('--all [target]', '拷贝整个 EDM 资源目录到当前或指定目录')
|
|
109
|
-
.action(async (initPath, options) => {
|
|
132
|
+
.action(safeAction(async (initPath, options) => {
|
|
110
133
|
const { runInitMode } = require('../src/init');
|
|
111
134
|
await runInitMode({
|
|
112
135
|
initPath: initPath || null,
|
|
@@ -115,11 +138,11 @@ program
|
|
|
115
138
|
config: options.config || null,
|
|
116
139
|
all: options.all !== undefined ? (options.all || true) : null,
|
|
117
140
|
});
|
|
118
|
-
});
|
|
141
|
+
}));
|
|
119
142
|
|
|
120
143
|
// ─── Default action (backward compatible) ───────────────────────────────
|
|
121
144
|
program
|
|
122
|
-
.action(async (options) => {
|
|
145
|
+
.action(safeAction(async (options) => {
|
|
123
146
|
// 注册/卸载右键菜单
|
|
124
147
|
if (options.install) {
|
|
125
148
|
const { registerContextMenu } = require('../src/context-menu');
|
|
@@ -161,7 +184,7 @@ program
|
|
|
161
184
|
// 全交互模式:无 --snippet,无 -f
|
|
162
185
|
const { runInteractiveMode } = require('../src/snippet');
|
|
163
186
|
await runInteractiveMode({ config: options.config });
|
|
164
|
-
});
|
|
187
|
+
}));
|
|
165
188
|
|
|
166
189
|
program.allowUnknownOption(false);
|
|
167
190
|
program.parse(process.argv);
|
package/package.json
CHANGED
package/src/init.js
CHANGED
|
@@ -93,13 +93,13 @@ async function directCopy(srcPath, cwd) {
|
|
|
93
93
|
* Wrap @inquirer/prompts select with back/exit navigation.
|
|
94
94
|
* Returns the chosen value, 'back', or 'exit'.
|
|
95
95
|
*/
|
|
96
|
-
async function selectWithNav(choices, showBack) {
|
|
96
|
+
async function selectWithNav(message, choices, showBack) {
|
|
97
97
|
const { select } = await import('@inquirer/prompts');
|
|
98
98
|
const all = [];
|
|
99
99
|
if (showBack) all.push({ name: '.. 返回上级', value: 'back' });
|
|
100
100
|
all.push(...choices);
|
|
101
101
|
all.push({ name: '✕ 退出', value: 'exit' });
|
|
102
|
-
return select({ message
|
|
102
|
+
return select({ message, choices: all, loop: false });
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
async function interactiveInit(edmDir, cwd) {
|
|
@@ -113,7 +113,7 @@ async function interactiveInit(edmDir, cwd) {
|
|
|
113
113
|
name: formatName(b.meta, b.name) + (b.meta.description ? ' — ' + chalk.dim(b.meta.description) : ''),
|
|
114
114
|
value: b,
|
|
115
115
|
}));
|
|
116
|
-
const result = await selectWithNav(choices, false);
|
|
116
|
+
const result = await selectWithNav('选择品牌:', choices, false);
|
|
117
117
|
if (result === 'exit') { console.log(chalk.gray('已退出。\n')); return; }
|
|
118
118
|
brand = result;
|
|
119
119
|
step = 'version';
|
|
@@ -126,7 +126,7 @@ async function interactiveInit(edmDir, cwd) {
|
|
|
126
126
|
name: formatName(v.meta, v.name) + (v.meta.description ? ' — ' + chalk.dim(v.meta.description) : ''),
|
|
127
127
|
value: v,
|
|
128
128
|
}));
|
|
129
|
-
const result = await selectWithNav(choices, true);
|
|
129
|
+
const result = await selectWithNav('选择模板版本:', choices, true);
|
|
130
130
|
if (result === 'back') { step = 'brand'; continue; }
|
|
131
131
|
if (result === 'exit') { console.log(chalk.gray('已退出。\n')); return; }
|
|
132
132
|
version = result;
|
|
@@ -148,7 +148,7 @@ async function interactiveInit(edmDir, cwd) {
|
|
|
148
148
|
value: s,
|
|
149
149
|
})),
|
|
150
150
|
];
|
|
151
|
-
const result = await selectWithNav(choices, true);
|
|
151
|
+
const result = await selectWithNav('选择片段系列:', choices, true);
|
|
152
152
|
if (result === 'back') { step = 'version'; continue; }
|
|
153
153
|
if (result === 'exit') { console.log(chalk.gray('已退出。\n')); return; }
|
|
154
154
|
series = result;
|
|
@@ -167,7 +167,7 @@ async function interactiveInit(edmDir, cwd) {
|
|
|
167
167
|
name: formatName(v.meta, v.name) + (v.meta.description ? ' — ' + chalk.dim(v.meta.description) : ''),
|
|
168
168
|
value: v,
|
|
169
169
|
}));
|
|
170
|
-
const result = await selectWithNav(choices, true);
|
|
170
|
+
const result = await selectWithNav('选择片段变体:', choices, true);
|
|
171
171
|
if (result === 'back') { step = 'series'; continue; }
|
|
172
172
|
if (result === 'exit') { console.log(chalk.gray('已退出。\n')); return; }
|
|
173
173
|
variant = result;
|
|
@@ -367,11 +367,13 @@ async function runInitMode({ initPath, template, snippet, config, all }) {
|
|
|
367
367
|
|
|
368
368
|
// --all: copy entire EDM directory
|
|
369
369
|
if (all) {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
370
|
+
// Always use package built-in EDM for --all (not CWD's local copy)
|
|
371
|
+
const pkgEdm = path.resolve(__dirname, '..', 'edm');
|
|
372
|
+
const edmDir = fs.existsSync(pkgEdm) ? pkgEdm : (() => {
|
|
373
|
+
try { return resolveEdmDir(); } catch (_) { return null; }
|
|
374
|
+
})();
|
|
375
|
+
if (!edmDir) {
|
|
376
|
+
console.error(chalk.red('\n ✘ EDM 资源库不存在,无法拷贝。\n'));
|
|
375
377
|
process.exit(1);
|
|
376
378
|
}
|
|
377
379
|
const target = all === true ? cwd : path.resolve(all);
|