Skip to content

Commit 291b8a7

Browse files
committed
project done ig
1 parent 6c5e8aa commit 291b8a7

File tree

7 files changed

+446
-332
lines changed

7 files changed

+446
-332
lines changed

demo.cac.ts

Lines changed: 74 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,143 +1,83 @@
1-
// import fs from "fs/promises";
2-
// import cac from "cac";
3-
// import {
4-
// Callback,
5-
// Completion,
6-
// flagMap,
7-
// Positional,
8-
// positionalMap,
9-
// } from "./shared";
10-
// import path from "path";
11-
// import tab from "./cac";
1+
import cac from "cac";
2+
import tab from "./src/cac";
123

13-
// const cli = cac("vite"); // Using 'vite' as the CLI tool name
4+
const cli = cac("vite");
145

15-
// // Custom converters (placeholders)
16-
// function convertBase(value) {
17-
// return value;
18-
// }
6+
cli
7+
.option("-c, --config <file>", `Use specified config file`)
8+
.option("-m, --mode <mode>", `Set env mode`)
9+
.option("-l, --logLevel <level>", `info | warn | error | silent`);
1910

20-
// function convertHost(value) {
21-
// return value;
22-
// }
11+
cli
12+
.command("dev", "Start dev server")
13+
.option("--host [host]", `Specify hostname`)
14+
.option("--port <port>", `Specify port`)
15+
.action((options) => {});
2316

24-
// // https://github.com/vitejs/vite/blob/main/packages/vite/src/node/cli.ts
25-
// // Global options
26-
// cli
27-
// .option("-c, --config <file>", `[string] use specified config file`)
28-
// .option("--base <path>", `[string] public base path (default: /)`, {
29-
// type: [convertBase],
30-
// })
31-
// .option("-l, --logLevel <level>", `[string] info | warn | error | silent`)
32-
// .option("--clearScreen", `[boolean] allow/disable clear screen when logging`)
33-
// .option("-d, --debug [feat]", `[string | boolean] show debug logs`)
34-
// .option("-f, --filter <filter>", `[string] filter debug logs`)
35-
// .option("-m, --mode <mode>", `[string] set env mode`);
17+
cli
18+
.command("dev build", "Build project")
19+
.action((options) => {});
3620

37-
// // Dev command
38-
// cli
39-
// .command("[root]", "start dev server") // default command
40-
// .alias("serve") // the command is called 'serve' in Vite's API
41-
// .alias("dev") // alias to align with the script name
42-
// .option("--host [host]", `[string] specify hostname`, { type: [convertHost] })
43-
// .option("--port <port>", `[number] specify port`)
44-
// .option("--open [path]", `[boolean | string] open browser on startup`)
45-
// .option("--cors", `[boolean] enable CORS`)
46-
// .option("--strictPort", `[boolean] exit if specified port is already in use`)
47-
// .option(
48-
// "--force",
49-
// `[boolean] force the optimizer to ignore the cache and re-bundle`
50-
// )
51-
// .action((root, options) => {
52-
// console.log(`Starting dev server at ${root || "."} with options:`, options);
53-
// });
54-
// // Build positional completions for each command using command.args
55-
// for (const c of [cli.globalCommand, ...cli.commands]) {
56-
// // Handle options
57-
// for (const o of [...cli.globalCommand.options, ...c.options]) {
58-
// const optionKey = `${c.name} ${o.name}`;
21+
cli
22+
.command("lint [...files]", "Lint project")
23+
.action((files, options) => {});
5924

60-
// if (o.rawName.includes("--logLevel <level>")) {
61-
// // Completion for --logLevel
62-
// flagMap.set(optionKey, async (previousArgs, toComplete) => {
63-
// return [
64-
// { action: "info", description: "Info level logging" },
65-
// { action: "warn", description: "Warning level logging" },
66-
// { action: "error", description: "Error level logging" },
67-
// { action: "silent", description: "No logging" },
68-
// ].filter((comp) => comp.action.startsWith(toComplete));
69-
// });
70-
// }
25+
const completion = await tab(cli);
7126

72-
// if (o.rawName.includes("--mode <mode>")) {
73-
// // Completion for --mode
74-
// flagMap.set(optionKey, async (previousArgs, toComplete) => {
75-
// return [
76-
// { action: "production", description: "Production mode" },
77-
// { action: "development", description: "Development mode" },
78-
// { action: "staging", description: "Staging mode" },
79-
// ].filter((comp) => comp.action.startsWith(toComplete));
80-
// });
81-
// }
27+
for (const command of completion.commands.values()) {
28+
if (command.name === 'lint') {
29+
command.handler = () => {
30+
return [
31+
{ value: 'main.ts', description: 'Main file' },
32+
{ value: 'index.ts', description: 'Index file' },
33+
];
34+
};
35+
}
8236

83-
// if (o.rawName.includes("--port <port>")) {
84-
// // Completion for --port
85-
// flagMap.set(optionKey, async (previousArgs, toComplete) => {
86-
// return [
87-
// { action: "3000", description: "Development server port" },
88-
// { action: "8080", description: "Alternative port" },
89-
// { action: "80", description: "HTTP port" },
90-
// { action: "443", description: "HTTPS port" },
91-
// { action: "5000", description: "Common backend port" },
92-
// ].filter((comp) => comp.action.startsWith(toComplete));
93-
// });
94-
// }
37+
for (const [o, config] of command.options.entries()) {
38+
if (o === '--port') {
39+
config.handler = () => {
40+
return [
41+
{ value: '3000', description: 'Development server port' },
42+
{ value: '8080', description: 'Alternative port' },
43+
];
44+
};
45+
}
46+
if (o === '--host') {
47+
config.handler = () => {
48+
return [
49+
{ value: 'localhost', description: 'Localhost' },
50+
{ value: '0.0.0.0', description: 'All interfaces' },
51+
];
52+
};
53+
}
54+
if (o === '--config') {
55+
config.handler = () => {
56+
return [
57+
{ value: 'vite.config.ts', description: 'Vite config file' },
58+
{ value: 'vite.config.js', description: 'Vite config file' },
59+
];
60+
};
61+
}
62+
if (o === '--mode') {
63+
config.handler = () => {
64+
return [
65+
{ value: 'development', description: 'Development mode' },
66+
{ value: 'production', description: 'Production mode' },
67+
];
68+
};
69+
}
70+
if (o === '--logLevel') {
71+
config.handler = () => {
72+
return [
73+
{ value: 'info', description: 'Info level' },
74+
{ value: 'warn', description: 'Warn level' },
75+
{ value: 'error', description: 'Error level' },
76+
{ value: 'silent', description: 'Silent level' },
77+
];
78+
};
79+
}
80+
}
81+
}
9582

96-
// if (o.rawName.includes("--host [host]")) {
97-
// // Completion for --host
98-
// flagMap.set(optionKey, async (previousArgs, toComplete) => {
99-
// return [
100-
// { action: "localhost", description: "Localhost" },
101-
// { action: "0.0.0.0", description: "All interfaces" },
102-
// { action: "127.0.0.1", description: "Loopback interface" },
103-
// ].filter((comp) => comp.action.startsWith(toComplete));
104-
// });
105-
// }
106-
107-
// if (o.rawName.includes("--config <file>")) {
108-
// // Completion for --config
109-
// flagMap.set(optionKey, async (previousArgs, toComplete) => {
110-
// const configFiles = ["vite.config.ts", "vite.config.js"].filter(
111-
// (file) => file.startsWith(toComplete)
112-
// );
113-
// return configFiles.map((file) => ({ action: file }));
114-
// });
115-
// }
116-
117-
// // Add more option completions as needed
118-
// }
119-
120-
// // Handle positional arguments
121-
// if (c.args && c.args.length > 0) {
122-
// const positionals = c.args.map((arg) => ({
123-
// required: arg.required,
124-
// variadic: arg.variadic,
125-
// value: arg.value,
126-
// completion: async (previousArgs, toComplete) => {
127-
// if (arg.value === "root") {
128-
// return [
129-
// { action: "src/", description: "💣️.sh loves vite!" },
130-
// { action: "./", description: "This one is better." },
131-
// ];
132-
// }
133-
// return [];
134-
// },
135-
// }));
136-
137-
// positionalMap.set(c.name, positionals);
138-
// }
139-
// }
140-
141-
// tab(cli);
142-
143-
// cli.parse();
83+
cli.parse();

demo.citty.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ for (const command of completion.commands.values()) {
6868

6969
if (command.name === 'lint') {
7070
command.handler = () => {
71-
console.log('lint handler')
7271
return [
7372
{ value: 'main.ts', description: 'Main file' },
7473
{ value: 'index.ts', description: 'Index file' },

src/cac.ts

Lines changed: 88 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,21 @@
1-
// // @bombsh/tab/cac
2-
// import { CAC } from "cac";
3-
// import * as zsh from "./zsh";
4-
// import * as bash from "./bash";
5-
// import * as fish from "./fish";
6-
// import * as powershell from "./powershell";
7-
// import {
8-
// flagMap,
9-
// Positional,
10-
// positionalMap,
11-
// ShellCompDirective,
12-
// } from "./shared";
13-
14-
// function quoteIfNeeded(path: string): string {
15-
// return path.includes(" ") ? `'${path}'` : path;
16-
// }
1+
import * as zsh from "./zsh";
2+
import * as bash from "./bash";
3+
import * as fish from "./fish";
4+
import * as powershell from "./powershell";
5+
import type { CAC } from "cac";
6+
import { Completion } from "./";
177

18-
// const execPath = process.execPath;
19-
// const processArgs = process.argv.slice(1);
8+
const execPath = process.execPath;
9+
const processArgs = process.argv.slice(1);
10+
const quotedExecPath = quoteIfNeeded(execPath);
11+
const quotedProcessArgs = processArgs.map(quoteIfNeeded);
12+
const quotedProcessExecArgs = process.execArgv.map(quoteIfNeeded);
2013

21-
// // Apply the quoting function to each part of x
22-
// // This ensures that paths like "Program Files" are quoted for PowerShell execution.
23-
// const quotedExecPath = quoteIfNeeded(execPath);
24-
// const quotedProcessArgs = processArgs.map(quoteIfNeeded);
25-
// const quotedProcessExecArgs = process.execArgv.map(quoteIfNeeded);
14+
const x = `${quotedExecPath} ${quotedProcessExecArgs.join(" ")} ${quotedProcessArgs[0]}`;
2615

27-
// const x = `${quotedExecPath} ${quotedProcessExecArgs.join(" ")} ${
28-
// quotedProcessArgs[0]
29-
// }`;
16+
function quoteIfNeeded(path: string): string {
17+
return path.includes(" ") ? `'${path}'` : path;
18+
}
3019

3120
// export default function tab(instance: CAC): void {
3221
// instance.command("complete [shell]").action(async (shell, extra) => {
@@ -302,3 +291,76 @@
302291
// }
303292
// });
304293
// }
294+
295+
export default function tab(instance: CAC): Completion {
296+
const completion = new Completion();
297+
298+
// Add all commands and their options
299+
for (const cmd of [instance.globalCommand, ...instance.commands]) {
300+
if (cmd.name === 'complete') continue; // Skip completion command
301+
302+
// Get positional args info from command usage
303+
const args = (cmd.rawName.match(/\[.*?\]|\<.*?\>/g) || [])
304+
.map(arg => arg.startsWith('[')); // true if optional (wrapped in [])
305+
306+
// Add command to completion
307+
const commandName = completion.addCommand(
308+
cmd.name === '@@global@@' ? '' : cmd.name,
309+
cmd.description || '',
310+
args,
311+
async () => []
312+
);
313+
314+
// Add command options
315+
for (const option of [...instance.globalCommand.options, ...cmd.options]) {
316+
completion.addOption(
317+
commandName,
318+
`--${option.name}`,
319+
option.description || '',
320+
async () => []
321+
);
322+
}
323+
}
324+
325+
instance.command("complete [shell]").action(async (shell, extra) => {
326+
switch (shell) {
327+
case "zsh": {
328+
const script = zsh.generate(instance.name, x);
329+
console.log(script);
330+
break;
331+
}
332+
case "bash": {
333+
const script = bash.generate(instance.name, x);
334+
console.log(script);
335+
break;
336+
}
337+
case "fish": {
338+
const script = fish.generate(instance.name, x);
339+
console.log(script);
340+
break;
341+
}
342+
case "powershell": {
343+
const script = powershell.generate(instance.name, x);
344+
console.log(script);
345+
break;
346+
}
347+
default: {
348+
const args: string[] = extra["--"];
349+
instance.showHelpOnExit = false;
350+
351+
// Parse current command context
352+
instance.unsetMatchedCommand();
353+
instance.parse([execPath, processArgs[0], ...args], {
354+
run: false,
355+
});
356+
357+
// const matchedCommand = instance.matchedCommand?.name || '';
358+
// const potentialCommand = args.join(' ')
359+
// console.log(potentialCommand)
360+
return completion.parse(args);
361+
}
362+
}
363+
});
364+
365+
return completion;
366+
}

src/citty.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,6 @@ export default async function tab<T extends ArgsDef = ArgsDef>(
167167
// TODO: `command lint i` does not work because `lint` and `i` are potential commands
168168
// instead the potential command should only be `lint`
169169
// and `i` is the to be completed part
170-
console.log('extra', parsed, ctx)
171170
return completion.parse(extra, matchedCommand);
172171
}
173172
}

0 commit comments

Comments
 (0)