@xmorse/cac 6.0.0 → 6.0.1
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/deno/CAC.ts +6 -15
- package/deno/Command.ts +20 -34
- package/deno/utils.ts +41 -1
- package/dist/index.js +40 -1
- package/dist/index.mjs +40 -1
- package/package.json +12 -11
package/deno/CAC.ts
CHANGED
|
@@ -160,27 +160,18 @@ class CAC extends EventEmitter {
|
|
|
160
160
|
}
|
|
161
161
|
let shouldParse = true;
|
|
162
162
|
|
|
163
|
-
// Sort by name length (longest first) so "mcp login" matches before "mcp"
|
|
164
|
-
const sortedCommands = [...this.commands].sort((a, b) => {
|
|
165
|
-
const aLength = a.name.split(' ').filter(Boolean).length;
|
|
166
|
-
const bLength = b.name.split(' ').filter(Boolean).length;
|
|
167
|
-
return bLength - aLength;
|
|
168
|
-
});
|
|
169
|
-
|
|
170
163
|
// Search sub-commands
|
|
171
|
-
for (const command of
|
|
164
|
+
for (const command of this.commands) {
|
|
172
165
|
const parsed = this.mri(argv.slice(2), command);
|
|
173
|
-
const
|
|
174
|
-
if (
|
|
166
|
+
const commandName = parsed.args[0];
|
|
167
|
+
if (command.isMatched(commandName)) {
|
|
175
168
|
shouldParse = false;
|
|
176
|
-
const matchedCommandName = parsed.args.slice(0, result.consumedArgs).join(' ');
|
|
177
169
|
const parsedInfo = {
|
|
178
170
|
...parsed,
|
|
179
|
-
args: parsed.args.slice(
|
|
171
|
+
args: parsed.args.slice(1)
|
|
180
172
|
};
|
|
181
|
-
this.setParsedInfo(parsedInfo, command,
|
|
182
|
-
this.emit(`command:${
|
|
183
|
-
break; // Stop after first match (greedy matching)
|
|
173
|
+
this.setParsedInfo(parsedInfo, command, commandName);
|
|
174
|
+
this.emit(`command:${commandName}`, command);
|
|
184
175
|
}
|
|
185
176
|
}
|
|
186
177
|
if (shouldParse) {
|
package/deno/Command.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import CAC from "./CAC.ts";
|
|
2
2
|
import Option, { OptionConfig } from "./Option.ts";
|
|
3
|
-
import { removeBrackets, findAllBrackets, findLongest, padRight, CACError } from "./utils.ts";
|
|
3
|
+
import { removeBrackets, findAllBrackets, findLongest, padRight, CACError, wrapText } from "./utils.ts";
|
|
4
4
|
import { platformInfo } from "./deno.ts";
|
|
5
5
|
interface CommandArg {
|
|
6
6
|
required: boolean;
|
|
@@ -77,38 +77,13 @@ class Command {
|
|
|
77
77
|
this.commandAction = callback;
|
|
78
78
|
return this;
|
|
79
79
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
matched: false,
|
|
88
|
-
consumedArgs: 0
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
if (args.length < nameParts.length) {
|
|
92
|
-
return {
|
|
93
|
-
matched: false,
|
|
94
|
-
consumedArgs: 0
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
for (let i = 0; i < nameParts.length; i++) {
|
|
98
|
-
if (nameParts[i] !== args[i]) {
|
|
99
|
-
if (i === 0 && this.aliasNames.includes(args[i])) {
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
return {
|
|
103
|
-
matched: false,
|
|
104
|
-
consumedArgs: 0
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return {
|
|
109
|
-
matched: true,
|
|
110
|
-
consumedArgs: nameParts.length
|
|
111
|
-
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Check if a command name is matched by this command
|
|
83
|
+
* @param name Command name
|
|
84
|
+
*/
|
|
85
|
+
isMatched(name: string) {
|
|
86
|
+
return this.name === name || this.aliasNames.includes(name);
|
|
112
87
|
}
|
|
113
88
|
get isDefaultCommand() {
|
|
114
89
|
return this.name === '' || this.aliasNames.includes('!');
|
|
@@ -144,13 +119,24 @@ class Command {
|
|
|
144
119
|
title: 'Usage',
|
|
145
120
|
body: ` $ ${name} ${this.usageText || this.rawName}`
|
|
146
121
|
});
|
|
122
|
+
|
|
123
|
+
// Show description for specific commands (not global/default)
|
|
124
|
+
if (!this.isGlobalCommand && !this.isDefaultCommand && this.description) {
|
|
125
|
+
const terminalWidth = process.stdout?.columns || 80;
|
|
126
|
+
sections.push({
|
|
127
|
+
title: 'Description',
|
|
128
|
+
body: wrapText(this.description, terminalWidth)
|
|
129
|
+
});
|
|
130
|
+
}
|
|
147
131
|
const showCommands = (this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0;
|
|
148
132
|
if (showCommands) {
|
|
149
133
|
const longestCommandName = findLongest(commands.map(command => command.rawName));
|
|
150
134
|
sections.push({
|
|
151
135
|
title: 'Commands',
|
|
152
136
|
body: commands.map(command => {
|
|
153
|
-
|
|
137
|
+
// Only show first line of description in commands listing
|
|
138
|
+
const firstLine = command.description.split('\n')[0].trim();
|
|
139
|
+
return ` ${padRight(command.rawName, longestCommandName.length)} ${firstLine}`;
|
|
154
140
|
}).join('\n')
|
|
155
141
|
});
|
|
156
142
|
sections.push({
|
package/deno/utils.ts
CHANGED
|
@@ -126,4 +126,44 @@ export class CACError extends Error {
|
|
|
126
126
|
this.stack = new Error(message).stack;
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
-
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Wrap text to fit within a given width, preserving existing line breaks
|
|
133
|
+
* and special formatting (lists, code blocks, etc.)
|
|
134
|
+
*/
|
|
135
|
+
export const wrapText = (text: string, width = 80, indent = ' '): string => {
|
|
136
|
+
const lines = text.split('\n');
|
|
137
|
+
const result: string[] = [];
|
|
138
|
+
for (const line of lines) {
|
|
139
|
+
// Preserve empty lines
|
|
140
|
+
if (line.trim() === '') {
|
|
141
|
+
result.push('');
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Preserve lines that start with special chars (bullets, headers, code, indented)
|
|
146
|
+
if (/^(\s*[-*>#`]|\s{4,}|#{1,6}\s)/.test(line)) {
|
|
147
|
+
result.push(indent + line);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Wrap normal paragraphs
|
|
152
|
+
const words = line.split(' ');
|
|
153
|
+
let currentLine = indent;
|
|
154
|
+
for (const word of words) {
|
|
155
|
+
if (currentLine.length + word.length + 1 > width) {
|
|
156
|
+
if (currentLine.trim()) {
|
|
157
|
+
result.push(currentLine);
|
|
158
|
+
}
|
|
159
|
+
currentLine = indent + word;
|
|
160
|
+
} else {
|
|
161
|
+
currentLine = currentLine === indent ? indent + word : `${currentLine} ${word}`;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (currentLine.trim()) {
|
|
165
|
+
result.push(currentLine);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return result.join('\n');
|
|
169
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,37 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var events = require('events');
|
|
6
6
|
|
|
7
|
+
function wrapText(text, width = 80, indent = ' ') {
|
|
8
|
+
const lines = text.split('\n');
|
|
9
|
+
const result = [];
|
|
10
|
+
for (const line of lines) {
|
|
11
|
+
if (line.trim() === '') {
|
|
12
|
+
result.push('');
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (/^(\s*[-*>#`]|\s{4,}|#{1,6}\s)/.test(line)) {
|
|
16
|
+
result.push(indent + line);
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const words = line.split(' ');
|
|
20
|
+
let currentLine = indent;
|
|
21
|
+
for (const word of words) {
|
|
22
|
+
if (currentLine.length + word.length + 1 > width) {
|
|
23
|
+
if (currentLine.trim()) {
|
|
24
|
+
result.push(currentLine);
|
|
25
|
+
}
|
|
26
|
+
currentLine = indent + word;
|
|
27
|
+
} else {
|
|
28
|
+
currentLine = currentLine === indent ? indent + word : `${currentLine} ${word}`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (currentLine.trim()) {
|
|
32
|
+
result.push(currentLine);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return result.join('\n');
|
|
36
|
+
}
|
|
37
|
+
|
|
7
38
|
function toArr(any) {
|
|
8
39
|
return any == null ? [] : Array.isArray(any) ? any : [any];
|
|
9
40
|
}
|
|
@@ -352,13 +383,21 @@ class Command {
|
|
|
352
383
|
title: "Usage",
|
|
353
384
|
body: ` $ ${name} ${this.usageText || this.rawName}`
|
|
354
385
|
});
|
|
386
|
+
if (!this.isGlobalCommand && !this.isDefaultCommand && this.description) {
|
|
387
|
+
const terminalWidth = process.stdout?.columns || 80;
|
|
388
|
+
sections.push({
|
|
389
|
+
title: "Description",
|
|
390
|
+
body: wrapText(this.description, terminalWidth)
|
|
391
|
+
});
|
|
392
|
+
}
|
|
355
393
|
const showCommands = (this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0;
|
|
356
394
|
if (showCommands) {
|
|
357
395
|
const longestCommandName = findLongest(commands.map((command) => command.rawName));
|
|
358
396
|
sections.push({
|
|
359
397
|
title: "Commands",
|
|
360
398
|
body: commands.map((command) => {
|
|
361
|
-
|
|
399
|
+
const firstLine = command.description.split('\n')[0].trim();
|
|
400
|
+
return ` ${padRight(command.rawName, longestCommandName.length)} ${firstLine}`;
|
|
362
401
|
}).join("\n")
|
|
363
402
|
});
|
|
364
403
|
sections.push({
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
2
|
|
|
3
|
+
function wrapText(text, width = 80, indent = ' ') {
|
|
4
|
+
const lines = text.split('\n');
|
|
5
|
+
const result = [];
|
|
6
|
+
for (const line of lines) {
|
|
7
|
+
if (line.trim() === '') {
|
|
8
|
+
result.push('');
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
if (/^(\s*[-*>#`]|\s{4,}|#{1,6}\s)/.test(line)) {
|
|
12
|
+
result.push(indent + line);
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const words = line.split(' ');
|
|
16
|
+
let currentLine = indent;
|
|
17
|
+
for (const word of words) {
|
|
18
|
+
if (currentLine.length + word.length + 1 > width) {
|
|
19
|
+
if (currentLine.trim()) {
|
|
20
|
+
result.push(currentLine);
|
|
21
|
+
}
|
|
22
|
+
currentLine = indent + word;
|
|
23
|
+
} else {
|
|
24
|
+
currentLine = currentLine === indent ? indent + word : `${currentLine} ${word}`;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (currentLine.trim()) {
|
|
28
|
+
result.push(currentLine);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return result.join('\n');
|
|
32
|
+
}
|
|
33
|
+
|
|
3
34
|
function toArr(any) {
|
|
4
35
|
return any == null ? [] : Array.isArray(any) ? any : [any];
|
|
5
36
|
}
|
|
@@ -348,13 +379,21 @@ class Command {
|
|
|
348
379
|
title: "Usage",
|
|
349
380
|
body: ` $ ${name} ${this.usageText || this.rawName}`
|
|
350
381
|
});
|
|
382
|
+
if (!this.isGlobalCommand && !this.isDefaultCommand && this.description) {
|
|
383
|
+
const terminalWidth = process.stdout?.columns || 80;
|
|
384
|
+
sections.push({
|
|
385
|
+
title: "Description",
|
|
386
|
+
body: wrapText(this.description, terminalWidth)
|
|
387
|
+
});
|
|
388
|
+
}
|
|
351
389
|
const showCommands = (this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0;
|
|
352
390
|
if (showCommands) {
|
|
353
391
|
const longestCommandName = findLongest(commands.map((command) => command.rawName));
|
|
354
392
|
sections.push({
|
|
355
393
|
title: "Commands",
|
|
356
394
|
body: commands.map((command) => {
|
|
357
|
-
|
|
395
|
+
const firstLine = command.description.split('\n')[0].trim();
|
|
396
|
+
return ` ${padRight(command.rawName, longestCommandName.length)} ${firstLine}`;
|
|
358
397
|
}).join("\n")
|
|
359
398
|
});
|
|
360
399
|
sections.push({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmorse/cac",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.1",
|
|
4
4
|
"description": "Simple yet powerful framework for building command-line apps.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "egoist/cac",
|
|
@@ -26,6 +26,16 @@
|
|
|
26
26
|
"/deno",
|
|
27
27
|
"/index-compat.js"
|
|
28
28
|
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"test": "jest",
|
|
31
|
+
"test:cov": "jest --coverage",
|
|
32
|
+
"build:deno": "node -r sucrase/register scripts/build-deno.ts",
|
|
33
|
+
"build:node": "rollup -c",
|
|
34
|
+
"build": "yarn build:deno && yarn build:node",
|
|
35
|
+
"toc": "markdown-toc -i README.md",
|
|
36
|
+
"prepublishOnly": "npm run build && cp mod.js mod.mjs",
|
|
37
|
+
"docs:api": "typedoc --out api-doc --readme none --exclude \"**/__test__/**\" --theme minimal"
|
|
38
|
+
},
|
|
29
39
|
"author": "egoist <0x142857@gmail.com>",
|
|
30
40
|
"license": "MIT",
|
|
31
41
|
"devDependencies": {
|
|
@@ -90,14 +100,5 @@
|
|
|
90
100
|
"hooks": {
|
|
91
101
|
"pre-commit": "npm t && lint-staged"
|
|
92
102
|
}
|
|
93
|
-
},
|
|
94
|
-
"scripts": {
|
|
95
|
-
"test": "jest",
|
|
96
|
-
"test:cov": "jest --coverage",
|
|
97
|
-
"build:deno": "node -r sucrase/register scripts/build-deno.ts",
|
|
98
|
-
"build:node": "rollup -c",
|
|
99
|
-
"build": "yarn build:deno && yarn build:node",
|
|
100
|
-
"toc": "markdown-toc -i README.md",
|
|
101
|
-
"docs:api": "typedoc --out api-doc --readme none --exclude \"**/__test__/**\" --theme minimal"
|
|
102
103
|
}
|
|
103
|
-
}
|
|
104
|
+
}
|