Skip to content

Commit 58c4924

Browse files
committed
update
1 parent 26ed777 commit 58c4924

File tree

1 file changed

+1
-196
lines changed

1 file changed

+1
-196
lines changed

bin/handlers/bun-handler.ts

Lines changed: 1 addition & 196 deletions
Original file line numberDiff line numberDiff line change
@@ -1,200 +1,5 @@
11
import type { PackageManagerCompletion } from '../package-manager-completion.js';
2-
import { stripAnsiEscapes, type ParsedOption } from '../utils/text-utils.js';
3-
import {
4-
LazyCommand,
5-
OptionHandlers,
6-
commonOptionHandlers,
7-
setupLazyOptionLoading,
8-
setupCommandArguments,
9-
safeExec,
10-
safeExecSync,
11-
} from '../utils/shared.js';
12-
13-
const COMMANDS_SECTION_RE = /^Commands:\s*$/i;
14-
const FLAGS_SECTION_RE = /^Flags:\s*$/i;
15-
const SECTION_END_RE = /^(Examples|Full documentation|Learn more)/i;
16-
const COMMAND_VALIDATION_RE = /^[a-z][a-z0-9-]*$/;
17-
const BUN_OPTION_RE =
18-
/^\s*(?:-([a-zA-Z]),?\s*)?--([a-z][a-z0-9-]*)(?:=<[^>]+>)?\s+(.+)$/;
19-
const MAIN_COMMAND_RE = /^ ([a-z][a-z0-9-]*)\s+(.+)$/;
20-
const CONTINUATION_COMMAND_RE = /^\s{12,}([a-z][a-z0-9-]*)\s+(.+)$/;
21-
const EMPTY_LINE_FOLLOWED_BY_NON_COMMAND_RE = /^\s+[a-z]/;
22-
const DESCRIPTION_SPLIT_RE = /\s{2,}/;
23-
const CAPITAL_LETTER_START_RE = /^[A-Z]/;
24-
const LINE_SPLIT_RE = /\r?\n/;
25-
26-
function toLines(text: string): string[] {
27-
return stripAnsiEscapes(text).split(LINE_SPLIT_RE);
28-
}
29-
30-
function findSectionStart(lines: string[], header: RegExp): number {
31-
for (let i = 0; i < lines.length; i++) {
32-
if (header.test(lines[i].trim())) return i + 1;
33-
}
34-
return -1;
35-
}
36-
37-
const bunOptionHandlers: OptionHandlers = {
38-
...commonOptionHandlers,
39-
40-
silent(complete) {
41-
complete('true', 'Enable silent mode');
42-
complete('false', 'Disable silent mode');
43-
},
44-
45-
backend(complete) {
46-
complete('clonefile', 'Clone files (default, fastest)');
47-
complete('hardlink', 'Use hard links');
48-
complete('symlink', 'Use symbolic links');
49-
complete('copyfile', 'Copy files');
50-
},
51-
52-
linker(complete) {
53-
complete('isolated', 'Isolated linker strategy');
54-
complete('hoisted', 'Hoisted linker strategy');
55-
},
56-
57-
omit(complete) {
58-
complete('dev', 'Omit devDependencies');
59-
complete('optional', 'Omit optionalDependencies');
60-
complete('peer', 'Omit peerDependencies');
61-
},
62-
63-
shell(complete) {
64-
complete('bun', 'Use Bun shell');
65-
complete('system', 'Use system shell');
66-
},
67-
68-
'unhandled-rejections'(complete) {
69-
complete('strict', 'Strict unhandled rejection handling');
70-
complete('throw', 'Throw on unhandled rejections');
71-
complete('warn', 'Warn on unhandled rejections');
72-
complete('none', 'Ignore unhandled rejections');
73-
complete('warn-with-error-code', 'Warn with error code');
74-
},
75-
};
76-
77-
/** ---------- Commands ---------- */
78-
export function parseBunHelp(helpText: string): Record<string, string> {
79-
const lines = toLines(helpText);
80-
81-
const startIndex = findSectionStart(lines, COMMANDS_SECTION_RE);
82-
if (startIndex === -1) return {};
83-
84-
const commands: Record<string, string> = {};
85-
86-
for (let i = startIndex; i < lines.length; i++) {
87-
const line = lines[i];
88-
89-
// stop when we hit Flags section or empty line followed by non-command content
90-
if (
91-
FLAGS_SECTION_RE.test(line.trim()) ||
92-
(line.trim() === '' &&
93-
i + 1 < lines.length &&
94-
!lines[i + 1].match(EMPTY_LINE_FOLLOWED_BY_NON_COMMAND_RE))
95-
) {
96-
break;
97-
}
98-
99-
if (!line.trim()) continue;
100-
101-
// main command row
102-
const main = line.match(MAIN_COMMAND_RE);
103-
if (main) {
104-
const [, command, rest] = main;
105-
if (COMMAND_VALIDATION_RE.test(command)) {
106-
const parts = rest.split(DESCRIPTION_SPLIT_RE);
107-
let desc = parts[parts.length - 1];
108-
109-
if (desc && CAPITAL_LETTER_START_RE.test(desc)) {
110-
commands[command] = desc.trim();
111-
} else if (parts.length > 1) {
112-
for (const p of parts) {
113-
if (CAPITAL_LETTER_START_RE.test(p)) {
114-
commands[command] = p.trim();
115-
break;
116-
}
117-
}
118-
}
119-
}
120-
}
121-
122-
const cont = line.match(CONTINUATION_COMMAND_RE);
123-
if (cont) {
124-
const [, command, description] = cont;
125-
if (COMMAND_VALIDATION_RE.test(command)) {
126-
commands[command] = description.trim();
127-
}
128-
}
129-
}
130-
131-
return commands;
132-
}
133-
134-
export async function getBunCommandsFromMainHelp(): Promise<
135-
Record<string, string>
136-
> {
137-
const output = await safeExec('bun --help');
138-
return output ? parseBunHelp(output) : {};
139-
}
140-
141-
export function parseBunOptions(
142-
helpText: string,
143-
{ flagsOnly = true }: { flagsOnly?: boolean } = {}
144-
): ParsedOption[] {
145-
const lines = toLines(helpText);
146-
const out: ParsedOption[] = [];
147-
148-
const start = findSectionStart(lines, FLAGS_SECTION_RE);
149-
if (start === -1) return out;
150-
151-
for (let i = start; i < lines.length; i++) {
152-
const line = lines[i];
153-
if (SECTION_END_RE.test(line.trim())) break;
154-
155-
const m = line.match(BUN_OPTION_RE);
156-
if (!m) continue;
157-
158-
const [, short, long, desc] = m;
159-
const takesValue = line.includes('=<'); // bun shows value as --opt=<val>
160-
if (flagsOnly && takesValue) continue;
161-
162-
out.push({
163-
short: short || undefined,
164-
long,
165-
desc: desc.trim(),
166-
});
167-
}
168-
169-
return out;
170-
}
171-
172-
function loadBunOptionsSync(cmd: LazyCommand, command: string): void {
173-
const output = safeExecSync(`bun ${command} --help`);
174-
if (!output) return;
175-
176-
const options = parseBunOptions(output, { flagsOnly: false });
177-
178-
for (const { long, short, desc } of options) {
179-
const exists = cmd.optionsRaw?.get?.(long);
180-
if (exists) continue;
181-
182-
const handler = bunOptionHandlers[long];
183-
if (handler) cmd.option(long, desc, handler, short);
184-
else cmd.option(long, desc, short);
185-
}
186-
}
1872

1883
export async function setupBunCompletions(
1894
completion: PackageManagerCompletion
190-
): Promise<void> {
191-
try {
192-
const commands = await getBunCommandsFromMainHelp();
193-
194-
for (const [command, description] of Object.entries(commands)) {
195-
const c = completion.command(command, description);
196-
setupCommandArguments(c, command, 'bun');
197-
setupLazyOptionLoading(c, command, 'bun', loadBunOptionsSync);
198-
}
199-
} catch {}
200-
}
5+
): Promise<void> { }

0 commit comments

Comments
 (0)