@emeryld/manager 0.6.5 → 0.6.7
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/helper-cli/display.js +1 -1
- package/dist/helper-cli/pagination.js +16 -12
- package/dist/helper-cli/prompts.js +120 -12
- package/dist/helper-cli.js +5 -3
- package/dist/menu/script-helpers.js +118 -11
- package/dist/menu.js +23 -51
- package/package.json +1 -1
|
@@ -45,5 +45,5 @@ export function printPaginatedScriptList(state, title) {
|
|
|
45
45
|
const display = option.enabled ? label : colors.dim(label);
|
|
46
46
|
console.log(` ${numberLabel} ${icon} ${display}`);
|
|
47
47
|
});
|
|
48
|
-
console.log(colors.dim(`Enter 1-${PAGE_SIZE} to run, ${PREVIOUS_KEY} for previous page, ${NEXT_KEY} for next page, ${BACK_KEY} to go back, or type a name.`));
|
|
48
|
+
console.log(colors.dim(`Enter 1-${PAGE_SIZE} to run, ${PREVIOUS_KEY} for previous page (if shown), ${NEXT_KEY} for next page (if shown), ${BACK_KEY} to go back, or type a name.`));
|
|
49
49
|
}
|
|
@@ -15,18 +15,22 @@ export function buildVisibleOptions(entries, page) {
|
|
|
15
15
|
}));
|
|
16
16
|
const hasPrevious = safePage > 0;
|
|
17
17
|
const hasNext = safePage < pageCount - 1;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
18
|
+
if (hasPrevious) {
|
|
19
|
+
options.push({
|
|
20
|
+
type: 'nav',
|
|
21
|
+
action: 'previous',
|
|
22
|
+
hotkey: PREVIOUS_KEY,
|
|
23
|
+
enabled: true,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
if (hasNext) {
|
|
27
|
+
options.push({
|
|
28
|
+
type: 'nav',
|
|
29
|
+
action: 'next',
|
|
30
|
+
hotkey: NEXT_KEY,
|
|
31
|
+
enabled: true,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
30
34
|
options.push({
|
|
31
35
|
type: 'nav',
|
|
32
36
|
action: 'back',
|
|
@@ -53,9 +53,16 @@ async function promptWithReadline(entries, title) {
|
|
|
53
53
|
rl.close();
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
function formatInteractiveLines(state, selectedIndex, title) {
|
|
56
|
+
function formatInteractiveLines(state, selectedIndex, title, searchState) {
|
|
57
57
|
const heading = pageHeading(title, state.page, state.pageCount);
|
|
58
58
|
const lines = [heading];
|
|
59
|
+
if (searchState?.active) {
|
|
60
|
+
const queryLabel = searchState.query || '(type to filter)';
|
|
61
|
+
const statusLabel = searchState.hasResults
|
|
62
|
+
? '(enter to run top match)'
|
|
63
|
+
: '(no matches yet)';
|
|
64
|
+
lines.push(colors.dim(`Search: ${queryLabel} ${statusLabel}`));
|
|
65
|
+
}
|
|
59
66
|
state.options.forEach((option, index) => {
|
|
60
67
|
const isSelected = index === selectedIndex;
|
|
61
68
|
const pointer = isSelected ? `${colors.green('➤')} ` : '';
|
|
@@ -69,7 +76,12 @@ function formatInteractiveLines(state, selectedIndex, title) {
|
|
|
69
76
|
: isSelected
|
|
70
77
|
? colors.green(option.entry.displayName)
|
|
71
78
|
: option.entry.displayName;
|
|
72
|
-
|
|
79
|
+
const runHint = searchState?.active &&
|
|
80
|
+
searchState.hasResults &&
|
|
81
|
+
index === 0
|
|
82
|
+
? colors.dim(' (enter to run)')
|
|
83
|
+
: '';
|
|
84
|
+
lines.push(`${pointer}${numberLabel}. ${option.entry.emoji} ${label} ${colors.dim(option.entry.metaLabel)}${runHint}`);
|
|
73
85
|
return;
|
|
74
86
|
}
|
|
75
87
|
const icon = option.action === 'back'
|
|
@@ -85,8 +97,17 @@ function formatInteractiveLines(state, selectedIndex, title) {
|
|
|
85
97
|
const navLabel = option.enabled ? baseLabel : colors.dim(baseLabel);
|
|
86
98
|
lines.push(`${pointer}${numberLabel}. ${icon} ${navLabel}`);
|
|
87
99
|
});
|
|
100
|
+
if (searchState?.active && !searchState.hasResults) {
|
|
101
|
+
lines.push(colors.yellow('No scripts match your search.'));
|
|
102
|
+
}
|
|
88
103
|
lines.push('');
|
|
89
|
-
lines.push(colors.dim(`Use ↑/↓ (or j/k) to move, 1-${PAGE_SIZE} to run, ${PREVIOUS_KEY} prev page, ${NEXT_KEY} next page, ${BACK_KEY} back, Enter to confirm, Esc/Ctrl+C to exit.`));
|
|
104
|
+
lines.push(colors.dim(`Use ↑/↓ (or j/k) to move, 1-${PAGE_SIZE} to run, ${PREVIOUS_KEY} prev page (when shown), ${NEXT_KEY} next page (when shown), ${BACK_KEY} back, Enter to confirm, Esc/Ctrl+C to exit.`));
|
|
105
|
+
if (searchState?.active) {
|
|
106
|
+
lines.push(colors.dim('Search mode: type to filter, Backspace clears query or exits, Enter runs the top match.'));
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
lines.push(colors.dim('Press Enter to search scripts by name.'));
|
|
110
|
+
}
|
|
90
111
|
return lines;
|
|
91
112
|
}
|
|
92
113
|
function renderInteractiveList(lines, previousLineCount) {
|
|
@@ -109,9 +130,15 @@ export async function promptForScript(entries, title) {
|
|
|
109
130
|
}
|
|
110
131
|
process.stdout.write('\x1b[?25l');
|
|
111
132
|
return new Promise((resolve) => {
|
|
133
|
+
const baseEntries = entries;
|
|
134
|
+
let filteredEntries = baseEntries;
|
|
112
135
|
let selectedIndex = 0;
|
|
113
136
|
let renderedLines = 0;
|
|
114
|
-
let state = buildVisibleOptions(
|
|
137
|
+
let state = buildVisibleOptions(filteredEntries, 0);
|
|
138
|
+
let searchActive = false;
|
|
139
|
+
let searchQuery = '';
|
|
140
|
+
let pageBeforeSearch = 0;
|
|
141
|
+
let selectionBeforeSearch = 0;
|
|
115
142
|
const cleanup = () => {
|
|
116
143
|
if (renderedLines > 0) {
|
|
117
144
|
process.stdout.write(`\x1b[${renderedLines}A`);
|
|
@@ -135,10 +162,24 @@ export async function promptForScript(entries, title) {
|
|
|
135
162
|
console.log();
|
|
136
163
|
resolve(undefined);
|
|
137
164
|
};
|
|
138
|
-
const
|
|
139
|
-
state = buildVisibleOptions(
|
|
165
|
+
const rebuildState = (page) => {
|
|
166
|
+
state = buildVisibleOptions(filteredEntries, page);
|
|
140
167
|
selectedIndex = Math.min(selectedIndex, state.options.length - 1);
|
|
141
168
|
selectedIndex = Math.max(0, selectedIndex);
|
|
169
|
+
};
|
|
170
|
+
const render = () => {
|
|
171
|
+
const searchState = searchActive
|
|
172
|
+
? {
|
|
173
|
+
active: true,
|
|
174
|
+
query: searchQuery.trim(),
|
|
175
|
+
hasResults: filteredEntries.length > 0,
|
|
176
|
+
}
|
|
177
|
+
: undefined;
|
|
178
|
+
const lines = formatInteractiveLines(state, selectedIndex, title, searchState);
|
|
179
|
+
renderedLines = renderInteractiveList(lines, renderedLines);
|
|
180
|
+
};
|
|
181
|
+
const setPage = (page) => {
|
|
182
|
+
rebuildState(page);
|
|
142
183
|
render();
|
|
143
184
|
};
|
|
144
185
|
const handleNav = (option) => {
|
|
@@ -167,9 +208,47 @@ export async function promptForScript(entries, title) {
|
|
|
167
208
|
}
|
|
168
209
|
handleNav(option);
|
|
169
210
|
};
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
|
|
211
|
+
const matchesSearchTerm = (entry, term) => {
|
|
212
|
+
const haystack = [
|
|
213
|
+
entry.displayName,
|
|
214
|
+
entry.metaLabel,
|
|
215
|
+
entry.script ?? '',
|
|
216
|
+
entry.description ?? '',
|
|
217
|
+
]
|
|
218
|
+
.join(' ')
|
|
219
|
+
.toLowerCase();
|
|
220
|
+
return haystack.includes(term);
|
|
221
|
+
};
|
|
222
|
+
const applySearch = () => {
|
|
223
|
+
const normalized = searchQuery.trim().toLowerCase();
|
|
224
|
+
if (normalized.length > 0) {
|
|
225
|
+
filteredEntries = baseEntries.filter((entry) => matchesSearchTerm(entry, normalized));
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
filteredEntries = baseEntries;
|
|
229
|
+
}
|
|
230
|
+
rebuildState(0);
|
|
231
|
+
selectedIndex = 0;
|
|
232
|
+
render();
|
|
233
|
+
};
|
|
234
|
+
const enterSearchMode = () => {
|
|
235
|
+
if (searchActive)
|
|
236
|
+
return;
|
|
237
|
+
pageBeforeSearch = state.page;
|
|
238
|
+
selectionBeforeSearch = selectedIndex;
|
|
239
|
+
searchActive = true;
|
|
240
|
+
searchQuery = '';
|
|
241
|
+
applySearch();
|
|
242
|
+
};
|
|
243
|
+
const exitSearchMode = () => {
|
|
244
|
+
if (!searchActive)
|
|
245
|
+
return;
|
|
246
|
+
searchActive = false;
|
|
247
|
+
searchQuery = '';
|
|
248
|
+
filteredEntries = baseEntries;
|
|
249
|
+
state = buildVisibleOptions(filteredEntries, pageBeforeSearch);
|
|
250
|
+
selectedIndex = Math.min(selectionBeforeSearch, state.options.length - 1);
|
|
251
|
+
render();
|
|
173
252
|
};
|
|
174
253
|
const onData = (buffer) => {
|
|
175
254
|
const isArrowUp = buffer.equals(Buffer.from([0x1b, 0x5b, 0x41]));
|
|
@@ -177,10 +256,39 @@ export async function promptForScript(entries, title) {
|
|
|
177
256
|
const isCtrlC = buffer.length === 1 && buffer[0] === 0x03;
|
|
178
257
|
const isEnter = buffer.length === 1 && (buffer[0] === 0x0d || buffer[0] === 0x0a);
|
|
179
258
|
const isEscape = buffer.length === 1 && buffer[0] === 0x1b;
|
|
259
|
+
const isBackspace = buffer.length === 1 && (buffer[0] === 0x7f || buffer[0] === 0x08);
|
|
260
|
+
const isPrintable = buffer.length === 1 && buffer[0] >= 0x20 && buffer[0] <= 0x7e;
|
|
180
261
|
if (isCtrlC || isEscape) {
|
|
181
262
|
cleanup();
|
|
182
263
|
process.exit(1);
|
|
183
264
|
}
|
|
265
|
+
if (searchActive) {
|
|
266
|
+
if (isEnter) {
|
|
267
|
+
if (filteredEntries.length > 0) {
|
|
268
|
+
commitSelection(filteredEntries[0]);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
process.stdout.write('\x07');
|
|
272
|
+
}
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (isBackspace) {
|
|
276
|
+
if (searchQuery.length > 0) {
|
|
277
|
+
searchQuery = searchQuery.slice(0, -1);
|
|
278
|
+
applySearch();
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
exitSearchMode();
|
|
282
|
+
}
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (isPrintable) {
|
|
286
|
+
searchQuery += String.fromCharCode(buffer[0]);
|
|
287
|
+
applySearch();
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
184
292
|
if (isArrowUp ||
|
|
185
293
|
(buffer.length === 1 && (buffer[0] === 0x6b || buffer[0] === 0x4b))) {
|
|
186
294
|
selectedIndex =
|
|
@@ -195,7 +303,7 @@ export async function promptForScript(entries, title) {
|
|
|
195
303
|
return;
|
|
196
304
|
}
|
|
197
305
|
if (isEnter) {
|
|
198
|
-
|
|
306
|
+
enterSearchMode();
|
|
199
307
|
return;
|
|
200
308
|
}
|
|
201
309
|
if (buffer.length === 1 && buffer[0] >= 0x30 && buffer[0] <= 0x39) {
|
|
@@ -211,10 +319,10 @@ export async function promptForScript(entries, title) {
|
|
|
211
319
|
((buffer[0] >= 0x41 && buffer[0] <= 0x5a) ||
|
|
212
320
|
(buffer[0] >= 0x61 && buffer[0] <= 0x7a))) {
|
|
213
321
|
const char = String.fromCharCode(buffer[0]).toLowerCase();
|
|
214
|
-
const foundIndex =
|
|
322
|
+
const foundIndex = baseEntries.findIndex((entry) => entry.displayName.toLowerCase().startsWith(char));
|
|
215
323
|
if (foundIndex !== -1) {
|
|
216
324
|
const page = Math.floor(foundIndex / PAGE_SIZE);
|
|
217
|
-
state = buildVisibleOptions(
|
|
325
|
+
state = buildVisibleOptions(filteredEntries, page);
|
|
218
326
|
selectedIndex = foundIndex % PAGE_SIZE;
|
|
219
327
|
render();
|
|
220
328
|
}
|
package/dist/helper-cli.js
CHANGED
|
@@ -14,7 +14,7 @@ export async function runHelperCli({ scripts, title = 'Helper CLI', argv = proce
|
|
|
14
14
|
const [firstArg, ...restArgs] = args;
|
|
15
15
|
if (firstArg === '--list' || firstArg === '-l') {
|
|
16
16
|
printScriptList(normalized, title);
|
|
17
|
-
return;
|
|
17
|
+
return false;
|
|
18
18
|
}
|
|
19
19
|
if (firstArg === '--help' || firstArg === '-h') {
|
|
20
20
|
console.log(colors.magenta('Usage:'));
|
|
@@ -22,19 +22,21 @@ export async function runHelperCli({ scripts, title = 'Helper CLI', argv = proce
|
|
|
22
22
|
console.log('\nFlags:');
|
|
23
23
|
console.log(' --list Show available scripts');
|
|
24
24
|
console.log(' --help Show this information');
|
|
25
|
-
return;
|
|
25
|
+
return false;
|
|
26
26
|
}
|
|
27
27
|
const argLooksLikeScript = firstArg && !firstArg.startsWith('-');
|
|
28
28
|
if (argLooksLikeScript) {
|
|
29
29
|
const requested = findScriptEntry(normalized, firstArg);
|
|
30
30
|
if (requested) {
|
|
31
31
|
await runEntry(requested, restArgs);
|
|
32
|
-
return;
|
|
32
|
+
return true;
|
|
33
33
|
}
|
|
34
34
|
console.log(colors.yellow(`Unknown script "${firstArg}". Falling back to interactive selection…`));
|
|
35
35
|
}
|
|
36
36
|
const selection = await promptForScript(normalized, title);
|
|
37
37
|
if (selection) {
|
|
38
38
|
await runEntry(selection, []);
|
|
39
|
+
return true;
|
|
39
40
|
}
|
|
41
|
+
return false;
|
|
40
42
|
}
|
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
import { colors } from "../utils/log.js";
|
|
2
2
|
import { run } from "../utils/run.js";
|
|
3
|
+
const FRONTEND_INDICATORS = [
|
|
4
|
+
{ dep: 'next', label: 'Next' },
|
|
5
|
+
{ dep: 'remix', label: 'Remix' },
|
|
6
|
+
{ dep: '@remix-run/node', label: 'Remix' },
|
|
7
|
+
{ dep: 'expo', label: 'Expo' },
|
|
8
|
+
{ dep: 'react-native', label: 'React Native' },
|
|
9
|
+
{ dep: 'react', label: 'React' },
|
|
10
|
+
{ dep: 'vite', label: 'Vite' },
|
|
11
|
+
{ dep: '@vitejs/plugin-react', label: 'Vite' },
|
|
12
|
+
{ dep: 'svelte', label: 'Svelte' },
|
|
13
|
+
{ dep: '@sveltejs/vite-plugin-svelte', label: 'SvelteKit' },
|
|
14
|
+
{ dep: '@angular/core', label: 'Angular' },
|
|
15
|
+
{ dep: 'vue', label: 'Vue' },
|
|
16
|
+
];
|
|
17
|
+
const BACKEND_INDICATORS = [
|
|
18
|
+
{ dep: 'express', label: 'Express' },
|
|
19
|
+
{ dep: 'fastify', label: 'Fastify' },
|
|
20
|
+
{ dep: 'koa', label: 'Koa' },
|
|
21
|
+
{ dep: 'hono', label: 'Hono' },
|
|
22
|
+
{ dep: '@nestjs/core', label: 'NestJS' },
|
|
23
|
+
{ dep: '@trpc/server', label: 'tRPC' },
|
|
24
|
+
];
|
|
25
|
+
const CLI_INDICATORS = [
|
|
26
|
+
{ dep: '@oclif/core', label: 'OClif' },
|
|
27
|
+
{ dep: '@oclif/command', label: 'OClif' },
|
|
28
|
+
{ dep: 'oclif', label: 'OClif' },
|
|
29
|
+
{ dep: 'commander', label: 'Commander' },
|
|
30
|
+
{ dep: 'yargs', label: 'Yargs' },
|
|
31
|
+
{ dep: 'cac', label: 'CAC' },
|
|
32
|
+
{ dep: 'clipanion', label: 'Clipanion' },
|
|
33
|
+
{ dep: 'caporal', label: 'Caporal' },
|
|
34
|
+
{ dep: 'ink', label: 'Ink' },
|
|
35
|
+
{ dep: 'meow', label: 'Meow' },
|
|
36
|
+
{ dep: 'enquirer', label: 'Enquirer' },
|
|
37
|
+
{ dep: 'zx', label: 'ZX' },
|
|
38
|
+
];
|
|
39
|
+
const CLI_KEYWORDS = new Set(['cli', 'command-line', 'commandline', 'tooling']);
|
|
40
|
+
const KIND_COLOR_MAP = {
|
|
41
|
+
frontend: colors.magenta,
|
|
42
|
+
backend: colors.green,
|
|
43
|
+
library: colors.cyan,
|
|
44
|
+
cli: colors.red,
|
|
45
|
+
};
|
|
3
46
|
export function collectDependencies(pkg) {
|
|
4
47
|
return new Set([
|
|
5
48
|
...Object.keys(pkg.json?.dependencies ?? {}),
|
|
@@ -37,6 +80,7 @@ function resolveFormatScript(pkg, scripts, dependencies) {
|
|
|
37
80
|
description: commandDescription(args),
|
|
38
81
|
args,
|
|
39
82
|
available: true,
|
|
83
|
+
isDefault: true,
|
|
40
84
|
};
|
|
41
85
|
}
|
|
42
86
|
return {
|
|
@@ -56,6 +100,7 @@ function resolveTypecheckScript(pkg, scripts, dependencies) {
|
|
|
56
100
|
description: commandDescription(args),
|
|
57
101
|
args,
|
|
58
102
|
available: true,
|
|
103
|
+
isDefault: true,
|
|
59
104
|
};
|
|
60
105
|
}
|
|
61
106
|
return {
|
|
@@ -75,6 +120,7 @@ function resolveLintScript(pkg, scripts, dependencies) {
|
|
|
75
120
|
description: commandDescription(args),
|
|
76
121
|
args,
|
|
77
122
|
available: true,
|
|
123
|
+
isDefault: true,
|
|
78
124
|
};
|
|
79
125
|
}
|
|
80
126
|
return {
|
|
@@ -86,7 +132,7 @@ function resolveLintScript(pkg, scripts, dependencies) {
|
|
|
86
132
|
function resolveDevScript(pkg, scripts, dependencies, kind) {
|
|
87
133
|
const devCandidates = kind === 'frontend'
|
|
88
134
|
? ['dev', 'start', 'serve']
|
|
89
|
-
: kind === 'backend'
|
|
135
|
+
: kind === 'backend' || kind === 'cli'
|
|
90
136
|
? ['dev', 'start', 'watch', 'serve', 'start:dev']
|
|
91
137
|
: ['dev', 'start'];
|
|
92
138
|
const direct = resolveRunScript(scripts, devCandidates);
|
|
@@ -99,6 +145,7 @@ function resolveDevScript(pkg, scripts, dependencies, kind) {
|
|
|
99
145
|
description: commandDescription(args),
|
|
100
146
|
args,
|
|
101
147
|
available: true,
|
|
148
|
+
isDefault: true,
|
|
102
149
|
};
|
|
103
150
|
}
|
|
104
151
|
if (dependencies.has('vite')) {
|
|
@@ -108,6 +155,7 @@ function resolveDevScript(pkg, scripts, dependencies, kind) {
|
|
|
108
155
|
description: commandDescription(args),
|
|
109
156
|
args,
|
|
110
157
|
available: true,
|
|
158
|
+
isDefault: true,
|
|
111
159
|
};
|
|
112
160
|
}
|
|
113
161
|
if (dependencies.has('expo')) {
|
|
@@ -117,6 +165,7 @@ function resolveDevScript(pkg, scripts, dependencies, kind) {
|
|
|
117
165
|
description: commandDescription(args),
|
|
118
166
|
args,
|
|
119
167
|
available: true,
|
|
168
|
+
isDefault: true,
|
|
120
169
|
};
|
|
121
170
|
}
|
|
122
171
|
return {
|
|
@@ -145,7 +194,9 @@ export function makeBaseScriptEntries(pkg) {
|
|
|
145
194
|
return BASE_SCRIPT_KEYS.map((key) => {
|
|
146
195
|
const resolution = resolveBaseScript(pkg, key, scripts, dependencies, marker.kind);
|
|
147
196
|
const description = resolution.available
|
|
148
|
-
? resolution.
|
|
197
|
+
? resolution.isDefault
|
|
198
|
+
? `${resolution.description} (default)`
|
|
199
|
+
: resolution.description
|
|
149
200
|
: `No ${key} command detected`;
|
|
150
201
|
return {
|
|
151
202
|
name: key,
|
|
@@ -162,17 +213,73 @@ export function makeBaseScriptEntries(pkg) {
|
|
|
162
213
|
});
|
|
163
214
|
}
|
|
164
215
|
export function getPackageMarker(pkg, dependencies = collectDependencies(pkg)) {
|
|
165
|
-
const
|
|
166
|
-
|
|
216
|
+
const normalizedDeps = new Set([...dependencies].map((dep) => String(dep).toLowerCase().trim()));
|
|
217
|
+
function findIndicator(indicators) {
|
|
218
|
+
for (const indicator of indicators) {
|
|
219
|
+
if (normalizedDeps.has(indicator.dep.toLowerCase())) {
|
|
220
|
+
return indicator;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
167
225
|
const pkgType = pkg.json?.type?.toLowerCase();
|
|
168
|
-
|
|
169
|
-
|
|
226
|
+
const keywords = (() => {
|
|
227
|
+
const raw = pkg.json?.keywords;
|
|
228
|
+
if (typeof raw === 'string') {
|
|
229
|
+
return raw
|
|
230
|
+
.split(/\s*,\s*/)
|
|
231
|
+
.map((keyword) => keyword.trim())
|
|
232
|
+
.filter(Boolean);
|
|
233
|
+
}
|
|
234
|
+
if (Array.isArray(raw)) {
|
|
235
|
+
return raw
|
|
236
|
+
.map((keyword) => String(keyword).trim())
|
|
237
|
+
.filter(Boolean);
|
|
238
|
+
}
|
|
239
|
+
return [];
|
|
240
|
+
})();
|
|
241
|
+
const hasCliKeyword = keywords
|
|
242
|
+
.map((keyword) => keyword.toLowerCase())
|
|
243
|
+
.some((keyword) => CLI_KEYWORDS.has(keyword));
|
|
244
|
+
const hasBinField = !!pkg.json?.bin &&
|
|
245
|
+
(typeof pkg.json?.bin === 'string' ||
|
|
246
|
+
typeof pkg.json?.bin === 'object' ||
|
|
247
|
+
Array.isArray(pkg.json?.bin));
|
|
248
|
+
const frontendIndicator = findIndicator(FRONTEND_INDICATORS);
|
|
249
|
+
if (frontendIndicator) {
|
|
250
|
+
return {
|
|
251
|
+
label: frontendIndicator.label ?? frontendIndicator.dep,
|
|
252
|
+
colorize: KIND_COLOR_MAP.frontend,
|
|
253
|
+
kind: 'frontend',
|
|
254
|
+
kindColorize: KIND_COLOR_MAP.frontend,
|
|
255
|
+
};
|
|
170
256
|
}
|
|
171
|
-
|
|
172
|
-
|
|
257
|
+
const backendIndicator = findIndicator(BACKEND_INDICATORS);
|
|
258
|
+
if (backendIndicator) {
|
|
259
|
+
return {
|
|
260
|
+
label: backendIndicator.label ?? backendIndicator.dep,
|
|
261
|
+
colorize: KIND_COLOR_MAP.backend,
|
|
262
|
+
kind: 'backend',
|
|
263
|
+
kindColorize: KIND_COLOR_MAP.backend,
|
|
264
|
+
};
|
|
173
265
|
}
|
|
174
|
-
|
|
175
|
-
|
|
266
|
+
const cliIndicator = findIndicator(CLI_INDICATORS);
|
|
267
|
+
if (cliIndicator || hasCliKeyword || hasBinField) {
|
|
268
|
+
const label = cliIndicator?.label ?? 'CLI';
|
|
269
|
+
return {
|
|
270
|
+
label,
|
|
271
|
+
colorize: KIND_COLOR_MAP.cli,
|
|
272
|
+
kind: 'cli',
|
|
273
|
+
kindColorize: KIND_COLOR_MAP.cli,
|
|
274
|
+
};
|
|
176
275
|
}
|
|
177
|
-
|
|
276
|
+
const isModule = pkgType === 'module';
|
|
277
|
+
const label = isModule ? 'ESM' : 'Node';
|
|
278
|
+
const labelColorizer = isModule ? colors.yellow : colors.cyan;
|
|
279
|
+
return {
|
|
280
|
+
label,
|
|
281
|
+
colorize: labelColorizer,
|
|
282
|
+
kind: 'library',
|
|
283
|
+
kindColorize: KIND_COLOR_MAP.library,
|
|
284
|
+
};
|
|
178
285
|
}
|
package/dist/menu.js
CHANGED
|
@@ -9,8 +9,12 @@ import { ensureWorkingTreeCommitted } from './preflight.js';
|
|
|
9
9
|
import { openDockerHelper } from './docker.js';
|
|
10
10
|
import { run } from './utils/run.js';
|
|
11
11
|
import { makeBaseScriptEntries, getPackageMarker } from './menu/script-helpers.js';
|
|
12
|
-
function
|
|
13
|
-
|
|
12
|
+
function formatKindLabel(kind) {
|
|
13
|
+
if (kind === 'cli')
|
|
14
|
+
return 'CLI';
|
|
15
|
+
return `${kind.charAt(0).toUpperCase()}${kind.slice(1)}`;
|
|
16
|
+
}
|
|
17
|
+
function makeManagerStepEntries(targets, packages, state) {
|
|
14
18
|
return [
|
|
15
19
|
{
|
|
16
20
|
name: 'update dependencies',
|
|
@@ -98,18 +102,6 @@ function makeManagerStepEntries(targets, packages, state, options) {
|
|
|
98
102
|
state.lastStep = 'full';
|
|
99
103
|
},
|
|
100
104
|
},
|
|
101
|
-
...(includeBack
|
|
102
|
-
? [
|
|
103
|
-
{
|
|
104
|
-
name: 'back',
|
|
105
|
-
emoji: '↩️',
|
|
106
|
-
description: 'Pick packages again',
|
|
107
|
-
handler: async () => {
|
|
108
|
-
state.lastStep = 'back';
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
]
|
|
112
|
-
: []),
|
|
113
105
|
];
|
|
114
106
|
}
|
|
115
107
|
function makeDockerEntry(pkg) {
|
|
@@ -154,12 +146,15 @@ export function buildPackageSelectionMenu(packages, onStepComplete) {
|
|
|
154
146
|
const entries = ordered.map((pkg) => {
|
|
155
147
|
const marker = getPackageMarker(pkg);
|
|
156
148
|
const pkgColor = pkg.color ?? 'cyan';
|
|
157
|
-
const descriptionMeta = pkg.relativeDir ?? pkg.dirName;
|
|
158
|
-
const
|
|
149
|
+
const descriptionMeta = colors.dim(pkg.relativeDir ?? pkg.dirName);
|
|
150
|
+
const labelBadge = marker.label ? marker.colorize(marker.label) : '';
|
|
151
|
+
const kindLabel = formatKindLabel(marker.kind);
|
|
152
|
+
const kindBadge = marker.kindColorize(kindLabel);
|
|
153
|
+
const badgeMeta = [labelBadge, kindBadge].filter(Boolean).join(' ');
|
|
159
154
|
return {
|
|
160
155
|
name: pkg.name ?? pkg.substitute ?? pkg.dirName,
|
|
161
156
|
emoji: marker.colorize('●'),
|
|
162
|
-
description:
|
|
157
|
+
description: [descriptionMeta, badgeMeta].filter(Boolean).join(' '),
|
|
163
158
|
color: pkgColor,
|
|
164
159
|
handler: async () => {
|
|
165
160
|
const step = await runStepLoop([pkg], packages);
|
|
@@ -199,19 +194,7 @@ export async function runStepLoop(targets, packages) {
|
|
|
199
194
|
emoji: globalEmoji,
|
|
200
195
|
description: 'update/test/build/publish',
|
|
201
196
|
handler: async () => {
|
|
202
|
-
const managerEntries =
|
|
203
|
-
...makeManagerStepEntries(targets, packages, state, {
|
|
204
|
-
includeBack: false,
|
|
205
|
-
}),
|
|
206
|
-
{
|
|
207
|
-
name: 'back',
|
|
208
|
-
emoji: '↩️',
|
|
209
|
-
description: 'Return to package menu',
|
|
210
|
-
handler: () => {
|
|
211
|
-
state.lastStep = undefined;
|
|
212
|
-
},
|
|
213
|
-
},
|
|
214
|
-
];
|
|
197
|
+
const managerEntries = makeManagerStepEntries(targets, packages, state);
|
|
215
198
|
await runHelperCli({
|
|
216
199
|
title: `Manager actions for ${pkg.name}`,
|
|
217
200
|
scripts: managerEntries,
|
|
@@ -228,38 +211,23 @@ export async function runStepLoop(targets, packages) {
|
|
|
228
211
|
console.log(colors.yellow(`No package.json scripts found for ${pkg.name}.`));
|
|
229
212
|
return;
|
|
230
213
|
}
|
|
231
|
-
const scriptsMenu = [
|
|
232
|
-
...scriptEntries,
|
|
233
|
-
{
|
|
234
|
-
name: 'back',
|
|
235
|
-
emoji: '↩️',
|
|
236
|
-
description: 'Return to package menu',
|
|
237
|
-
handler: () => { },
|
|
238
|
-
},
|
|
239
|
-
];
|
|
240
214
|
await runHelperCli({
|
|
241
215
|
title: `${pkg.name} scripts`,
|
|
242
|
-
scripts:
|
|
216
|
+
scripts: scriptEntries,
|
|
243
217
|
argv: [],
|
|
244
218
|
});
|
|
245
219
|
},
|
|
246
220
|
},
|
|
247
|
-
{
|
|
248
|
-
name: 'back',
|
|
249
|
-
emoji: '↩️',
|
|
250
|
-
description: 'Pick packages again',
|
|
251
|
-
handler: () => {
|
|
252
|
-
state.lastStep = 'back';
|
|
253
|
-
},
|
|
254
|
-
},
|
|
255
221
|
];
|
|
256
|
-
await runHelperCli({
|
|
222
|
+
const ranScript = await runHelperCli({
|
|
257
223
|
title: `Actions for ${pkg.name}`,
|
|
258
224
|
scripts: entries,
|
|
259
225
|
argv: [],
|
|
260
226
|
});
|
|
261
|
-
if (
|
|
227
|
+
if (!ranScript) {
|
|
228
|
+
state.lastStep = 'back';
|
|
262
229
|
return state.lastStep;
|
|
230
|
+
}
|
|
263
231
|
// loop to keep showing menu
|
|
264
232
|
}
|
|
265
233
|
}
|
|
@@ -271,11 +239,15 @@ export async function runStepLoop(targets, packages) {
|
|
|
271
239
|
while (true) {
|
|
272
240
|
state.lastStep = undefined;
|
|
273
241
|
const entries = makeManagerStepEntries(targets, packages, state);
|
|
274
|
-
await runHelperCli({
|
|
242
|
+
const ranScript = await runHelperCli({
|
|
275
243
|
title: `Actions for ${targets.length === 1 ? targets[0].name : 'selected packages'}`,
|
|
276
244
|
scripts: entries,
|
|
277
245
|
argv: [], // <- key change
|
|
278
246
|
});
|
|
247
|
+
if (!ranScript) {
|
|
248
|
+
state.lastStep = 'back';
|
|
249
|
+
return state.lastStep;
|
|
250
|
+
}
|
|
279
251
|
if (state.lastStep === 'back')
|
|
280
252
|
return state.lastStep;
|
|
281
253
|
// keep looping to show menu again
|