juice-email-cli 2.3.11 → 2.3.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.
- package/package.json +1 -1
- package/src/init.js +2 -4
- package/src/snippet.js +64 -16
package/package.json
CHANGED
package/src/init.js
CHANGED
|
@@ -231,10 +231,8 @@ async function interactiveInit(edmDir, cwd) {
|
|
|
231
231
|
}
|
|
232
232
|
if (action === 'snippet-config') {
|
|
233
233
|
selected = ['snippet', 'config'];
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (selected.length === 0) {
|
|
234
|
+
// fall through to confirmation below
|
|
235
|
+
} else if (selected.length === 0) {
|
|
238
236
|
console.log(chalk.gray('未选择任何内容。\n'));
|
|
239
237
|
step = series ? 'variant' : 'series';
|
|
240
238
|
break;
|
package/src/snippet.js
CHANGED
|
@@ -186,9 +186,9 @@ function findSnippetVariants(seriesDir) {
|
|
|
186
186
|
|
|
187
187
|
/**
|
|
188
188
|
* 列出变体目录下所有 YAML 配置,标记 juice.yaml 为最优配对。
|
|
189
|
-
* 返回 { name, path, isOptimal }。
|
|
189
|
+
* 返回 { name, path, isOptimal, source }。
|
|
190
190
|
*/
|
|
191
|
-
function findConfigs(variantDir) {
|
|
191
|
+
function findConfigs(variantDir, sourceLabel) {
|
|
192
192
|
const yamlFiles = findYamlFiles(variantDir);
|
|
193
193
|
return yamlFiles
|
|
194
194
|
.filter((f) => f.name !== '_meta.yaml' && f.name !== '_meta.yml')
|
|
@@ -196,9 +196,32 @@ function findConfigs(variantDir) {
|
|
|
196
196
|
name: f.name,
|
|
197
197
|
path: f.path,
|
|
198
198
|
isOptimal: f.name === 'juice.yaml',
|
|
199
|
+
source: sourceLabel || path.basename(path.dirname(f.path)),
|
|
199
200
|
}));
|
|
200
201
|
}
|
|
201
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Collect configs from multiple directories, deduplicating by path.
|
|
205
|
+
* Returns { name, path, isOptimal, source }[].
|
|
206
|
+
*/
|
|
207
|
+
function collectConfigs(dirs) {
|
|
208
|
+
const seen = new Set();
|
|
209
|
+
const result = [];
|
|
210
|
+
for (const { dir, label, markOptimal } of dirs) {
|
|
211
|
+
if (!fs.existsSync(dir)) continue;
|
|
212
|
+
const configs = findConfigs(dir, label);
|
|
213
|
+
for (const c of configs) {
|
|
214
|
+
if (seen.has(c.path)) continue;
|
|
215
|
+
seen.add(c.path);
|
|
216
|
+
if (markOptimal && c.name === 'juice.yaml') {
|
|
217
|
+
c.isOptimal = true;
|
|
218
|
+
}
|
|
219
|
+
result.push(c);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return result;
|
|
223
|
+
}
|
|
224
|
+
|
|
202
225
|
function findFiles(dir, regex) {
|
|
203
226
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
204
227
|
return entries
|
|
@@ -525,21 +548,42 @@ async function promptSnippetVariant(variants) {
|
|
|
525
548
|
});
|
|
526
549
|
}
|
|
527
550
|
|
|
528
|
-
async function promptConfig(configs) {
|
|
551
|
+
async function promptConfig(configs, preferredPath) {
|
|
529
552
|
const { select, input } = await import('@inquirer/prompts');
|
|
530
553
|
|
|
554
|
+
// Check if any names collide — if so, show source in the label
|
|
555
|
+
const nameCounts = {};
|
|
556
|
+
for (const c of configs) {
|
|
557
|
+
nameCounts[c.name] = (nameCounts[c.name] || 0) + 1;
|
|
558
|
+
}
|
|
559
|
+
const hasCollisions = Object.values(nameCounts).some(n => n > 1);
|
|
560
|
+
|
|
531
561
|
const choices = [];
|
|
532
562
|
let defaultIdx = 0;
|
|
563
|
+
let preferredIdx = -1;
|
|
533
564
|
|
|
534
565
|
for (const c of configs) {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
if (c.isOptimal)
|
|
566
|
+
let label = c.name;
|
|
567
|
+
if (hasCollisions && c.source) {
|
|
568
|
+
label += ` ${chalk.dim('(' + c.source + ')')}`;
|
|
569
|
+
}
|
|
570
|
+
const isPreferred = preferredPath && c.path === preferredPath;
|
|
571
|
+
if (isPreferred) preferredIdx = choices.length;
|
|
572
|
+
if (isPreferred || c.isOptimal) {
|
|
573
|
+
const tag = isPreferred ? '指定' : '最优配对';
|
|
574
|
+
choices.push({
|
|
575
|
+
name: `${chalk.green('●')} ${label} ${chalk.green('(' + tag + ')')}`,
|
|
576
|
+
value: { type: 'file', path: c.path, name: c.name },
|
|
577
|
+
});
|
|
578
|
+
} else {
|
|
579
|
+
choices.push({
|
|
580
|
+
name: ` ${label}`,
|
|
581
|
+
value: { type: 'file', path: c.path, name: c.name },
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
if (!isPreferred && c.isOptimal) defaultIdx = choices.length - 1;
|
|
542
585
|
}
|
|
586
|
+
if (preferredIdx >= 0) defaultIdx = preferredIdx;
|
|
543
587
|
|
|
544
588
|
choices.push(
|
|
545
589
|
{ name: ' [自定义] 输入其他路径...', value: { type: 'custom' } },
|
|
@@ -720,16 +764,19 @@ async function runSnippetMode({ snippet, template, config: cliConfigPath, output
|
|
|
720
764
|
templatePath = version.templatePath;
|
|
721
765
|
}
|
|
722
766
|
|
|
723
|
-
//
|
|
767
|
+
// 配置文件:收集当前目录和片段目录的配置,片段目录优先
|
|
724
768
|
const snippetDir = path.dirname(snippetPath);
|
|
725
769
|
let priorityConfigPath;
|
|
726
770
|
|
|
727
771
|
if (template) {
|
|
728
772
|
// 命令行完整指定(-s + -f),自动检测
|
|
729
|
-
priorityConfigPath = findLocalConfig(snippetDir);
|
|
773
|
+
priorityConfigPath = findLocalConfig(snippetDir) || findLocalConfig(process.cwd());
|
|
730
774
|
} else {
|
|
731
|
-
// 交互模式(只有 -s
|
|
732
|
-
const configs =
|
|
775
|
+
// 交互模式(只有 -s),收集 CWD + 片段目录配置
|
|
776
|
+
const configs = collectConfigs([
|
|
777
|
+
{ dir: snippetDir, label: path.basename(snippetDir), markOptimal: true },
|
|
778
|
+
{ dir: process.cwd(), label: '当前目录' },
|
|
779
|
+
]);
|
|
733
780
|
const configChoice = await promptConfig(configs);
|
|
734
781
|
priorityConfigPath = (configChoice.type === 'file') ? configChoice.path : null;
|
|
735
782
|
}
|
|
@@ -834,8 +881,8 @@ async function runInteractiveMode({ config: cliConfigPath }) {
|
|
|
834
881
|
}
|
|
835
882
|
|
|
836
883
|
// 5. 选择配置文件
|
|
837
|
-
const configs = findConfigs(variant.path);
|
|
838
|
-
const configChoice = await promptConfig(configs,
|
|
884
|
+
const configs = findConfigs(variant.path, path.basename(variant.path));
|
|
885
|
+
const configChoice = await promptConfig(configs, cliConfigPath);
|
|
839
886
|
|
|
840
887
|
let priorityConfigPath = null;
|
|
841
888
|
let configFileName = '(跳过)';
|
|
@@ -895,6 +942,7 @@ module.exports = {
|
|
|
895
942
|
findHtmlFiles,
|
|
896
943
|
findYamlFiles,
|
|
897
944
|
findLocalConfig,
|
|
945
|
+
collectConfigs,
|
|
898
946
|
getBrand,
|
|
899
947
|
filterSeries,
|
|
900
948
|
// config
|