index.cjs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. 'use strict';
  2. const consola = require('consola');
  3. const utils = require('consola/utils');
  4. function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
  5. const consola__default = /*#__PURE__*/_interopDefaultCompat(consola);
  6. function toArray(val) {
  7. if (Array.isArray(val)) {
  8. return val;
  9. }
  10. return val === void 0 ? [] : [val];
  11. }
  12. function formatLineColumns(lines, linePrefix = "") {
  13. const maxLengh = [];
  14. for (const line of lines) {
  15. for (const [i, element] of line.entries()) {
  16. maxLengh[i] = Math.max(maxLengh[i] || 0, element.length);
  17. }
  18. }
  19. return lines.map(
  20. (l) => l.map(
  21. (c, i) => linePrefix + c[i === 0 ? "padStart" : "padEnd"](maxLengh[i])
  22. ).join(" ")
  23. ).join("\n");
  24. }
  25. function resolveValue(input) {
  26. return typeof input === "function" ? input() : input;
  27. }
  28. class CLIError extends Error {
  29. constructor(message, code) {
  30. super(message);
  31. this.code = code;
  32. this.name = "CLIError";
  33. }
  34. }
  35. const NUMBER_CHAR_RE = /\d/;
  36. const STR_SPLITTERS = ["-", "_", "/", "."];
  37. function isUppercase(char = "") {
  38. if (NUMBER_CHAR_RE.test(char)) {
  39. return void 0;
  40. }
  41. return char !== char.toLowerCase();
  42. }
  43. function splitByCase(str, separators) {
  44. const splitters = separators ?? STR_SPLITTERS;
  45. const parts = [];
  46. if (!str || typeof str !== "string") {
  47. return parts;
  48. }
  49. let buff = "";
  50. let previousUpper;
  51. let previousSplitter;
  52. for (const char of str) {
  53. const isSplitter = splitters.includes(char);
  54. if (isSplitter === true) {
  55. parts.push(buff);
  56. buff = "";
  57. previousUpper = void 0;
  58. continue;
  59. }
  60. const isUpper = isUppercase(char);
  61. if (previousSplitter === false) {
  62. if (previousUpper === false && isUpper === true) {
  63. parts.push(buff);
  64. buff = char;
  65. previousUpper = isUpper;
  66. continue;
  67. }
  68. if (previousUpper === true && isUpper === false && buff.length > 1) {
  69. const lastChar = buff.at(-1);
  70. parts.push(buff.slice(0, Math.max(0, buff.length - 1)));
  71. buff = lastChar + char;
  72. previousUpper = isUpper;
  73. continue;
  74. }
  75. }
  76. buff += char;
  77. previousUpper = isUpper;
  78. previousSplitter = isSplitter;
  79. }
  80. parts.push(buff);
  81. return parts;
  82. }
  83. function upperFirst(str) {
  84. return str ? str[0].toUpperCase() + str.slice(1) : "";
  85. }
  86. function lowerFirst(str) {
  87. return str ? str[0].toLowerCase() + str.slice(1) : "";
  88. }
  89. function pascalCase(str, opts) {
  90. return str ? (Array.isArray(str) ? str : splitByCase(str)).map((p) => upperFirst(opts?.normalize ? p.toLowerCase() : p)).join("") : "";
  91. }
  92. function camelCase(str, opts) {
  93. return lowerFirst(pascalCase(str || "", opts));
  94. }
  95. function kebabCase(str, joiner) {
  96. return str ? (Array.isArray(str) ? str : splitByCase(str)).map((p) => p.toLowerCase()).join(joiner ?? "-") : "";
  97. }
  98. function toArr(any) {
  99. return any == void 0 ? [] : Array.isArray(any) ? any : [any];
  100. }
  101. function toVal(out, key, val, opts) {
  102. let x;
  103. const old = out[key];
  104. const nxt = ~opts.string.indexOf(key) ? val == void 0 || val === true ? "" : String(val) : typeof val === "boolean" ? val : ~opts.boolean.indexOf(key) ? val === "false" ? false : val === "true" || (out._.push((x = +val, x * 0 === 0) ? x : val), !!val) : (x = +val, x * 0 === 0) ? x : val;
  105. out[key] = old == void 0 ? nxt : Array.isArray(old) ? old.concat(nxt) : [old, nxt];
  106. }
  107. function parseRawArgs(args = [], opts = {}) {
  108. let k;
  109. let arr;
  110. let arg;
  111. let name;
  112. let val;
  113. const out = { _: [] };
  114. let i = 0;
  115. let j = 0;
  116. let idx = 0;
  117. const len = args.length;
  118. const alibi = opts.alias !== void 0;
  119. const strict = opts.unknown !== void 0;
  120. const defaults = opts.default !== void 0;
  121. opts.alias = opts.alias || {};
  122. opts.string = toArr(opts.string);
  123. opts.boolean = toArr(opts.boolean);
  124. if (alibi) {
  125. for (k in opts.alias) {
  126. arr = opts.alias[k] = toArr(opts.alias[k]);
  127. for (i = 0; i < arr.length; i++) {
  128. (opts.alias[arr[i]] = arr.concat(k)).splice(i, 1);
  129. }
  130. }
  131. }
  132. for (i = opts.boolean.length; i-- > 0; ) {
  133. arr = opts.alias[opts.boolean[i]] || [];
  134. for (j = arr.length; j-- > 0; ) {
  135. opts.boolean.push(arr[j]);
  136. }
  137. }
  138. for (i = opts.string.length; i-- > 0; ) {
  139. arr = opts.alias[opts.string[i]] || [];
  140. for (j = arr.length; j-- > 0; ) {
  141. opts.string.push(arr[j]);
  142. }
  143. }
  144. if (defaults) {
  145. for (k in opts.default) {
  146. name = typeof opts.default[k];
  147. arr = opts.alias[k] = opts.alias[k] || [];
  148. if (opts[name] !== void 0) {
  149. opts[name].push(k);
  150. for (i = 0; i < arr.length; i++) {
  151. opts[name].push(arr[i]);
  152. }
  153. }
  154. }
  155. }
  156. const keys = strict ? Object.keys(opts.alias) : [];
  157. for (i = 0; i < len; i++) {
  158. arg = args[i];
  159. if (arg === "--") {
  160. out._ = out._.concat(args.slice(++i));
  161. break;
  162. }
  163. for (j = 0; j < arg.length; j++) {
  164. if (arg.charCodeAt(j) !== 45) {
  165. break;
  166. }
  167. }
  168. if (j === 0) {
  169. out._.push(arg);
  170. } else if (arg.substring(j, j + 3) === "no-") {
  171. name = arg.slice(Math.max(0, j + 3));
  172. if (strict && !~keys.indexOf(name)) {
  173. return opts.unknown(arg);
  174. }
  175. out[name] = false;
  176. } else {
  177. for (idx = j + 1; idx < arg.length; idx++) {
  178. if (arg.charCodeAt(idx) === 61) {
  179. break;
  180. }
  181. }
  182. name = arg.substring(j, idx);
  183. val = arg.slice(Math.max(0, ++idx)) || i + 1 === len || ("" + args[i + 1]).charCodeAt(0) === 45 || args[++i];
  184. arr = j === 2 ? [name] : name;
  185. for (idx = 0; idx < arr.length; idx++) {
  186. name = arr[idx];
  187. if (strict && !~keys.indexOf(name)) {
  188. return opts.unknown("-".repeat(j) + name);
  189. }
  190. toVal(out, name, idx + 1 < arr.length || val, opts);
  191. }
  192. }
  193. }
  194. if (defaults) {
  195. for (k in opts.default) {
  196. if (out[k] === void 0) {
  197. out[k] = opts.default[k];
  198. }
  199. }
  200. }
  201. if (alibi) {
  202. for (k in out) {
  203. arr = opts.alias[k] || [];
  204. while (arr.length > 0) {
  205. out[arr.shift()] = out[k];
  206. }
  207. }
  208. }
  209. return out;
  210. }
  211. function parseArgs(rawArgs, argsDef) {
  212. const parseOptions = {
  213. boolean: [],
  214. string: [],
  215. mixed: [],
  216. alias: {},
  217. default: {}
  218. };
  219. const args = resolveArgs(argsDef);
  220. for (const arg of args) {
  221. if (arg.type === "positional") {
  222. continue;
  223. }
  224. if (arg.type === "string") {
  225. parseOptions.string.push(arg.name);
  226. } else if (arg.type === "boolean") {
  227. parseOptions.boolean.push(arg.name);
  228. }
  229. if (arg.default !== void 0) {
  230. parseOptions.default[arg.name] = arg.default;
  231. }
  232. if (arg.alias) {
  233. parseOptions.alias[arg.name] = arg.alias;
  234. }
  235. }
  236. const parsed = parseRawArgs(rawArgs, parseOptions);
  237. const [...positionalArguments] = parsed._;
  238. const parsedArgsProxy = new Proxy(parsed, {
  239. get(target, prop) {
  240. return target[prop] ?? target[camelCase(prop)] ?? target[kebabCase(prop)];
  241. }
  242. });
  243. for (const [, arg] of args.entries()) {
  244. if (arg.type === "positional") {
  245. const nextPositionalArgument = positionalArguments.shift();
  246. if (nextPositionalArgument !== void 0) {
  247. parsedArgsProxy[arg.name] = nextPositionalArgument;
  248. } else if (arg.default === void 0 && arg.required !== false) {
  249. throw new CLIError(
  250. `Missing required positional argument: ${arg.name.toUpperCase()}`,
  251. "EARG"
  252. );
  253. } else {
  254. parsedArgsProxy[arg.name] = arg.default;
  255. }
  256. } else if (arg.required && parsedArgsProxy[arg.name] === void 0) {
  257. throw new CLIError(`Missing required argument: --${arg.name}`, "EARG");
  258. }
  259. }
  260. return parsedArgsProxy;
  261. }
  262. function resolveArgs(argsDef) {
  263. const args = [];
  264. for (const [name, argDef] of Object.entries(argsDef || {})) {
  265. args.push({
  266. ...argDef,
  267. name,
  268. alias: toArray(argDef.alias)
  269. });
  270. }
  271. return args;
  272. }
  273. function defineCommand(def) {
  274. return def;
  275. }
  276. async function runCommand(cmd, opts) {
  277. const cmdArgs = await resolveValue(cmd.args || {});
  278. const parsedArgs = parseArgs(opts.rawArgs, cmdArgs);
  279. const context = {
  280. rawArgs: opts.rawArgs,
  281. args: parsedArgs,
  282. data: opts.data,
  283. cmd
  284. };
  285. if (typeof cmd.setup === "function") {
  286. await cmd.setup(context);
  287. }
  288. let result;
  289. try {
  290. const subCommands = await resolveValue(cmd.subCommands);
  291. if (subCommands && Object.keys(subCommands).length > 0) {
  292. const subCommandArgIndex = opts.rawArgs.findIndex(
  293. (arg) => !arg.startsWith("-")
  294. );
  295. const subCommandName = opts.rawArgs[subCommandArgIndex];
  296. if (subCommandName) {
  297. if (!subCommands[subCommandName]) {
  298. throw new CLIError(
  299. `Unknown command \`${subCommandName}\``,
  300. "E_UNKNOWN_COMMAND"
  301. );
  302. }
  303. const subCommand = await resolveValue(subCommands[subCommandName]);
  304. if (subCommand) {
  305. await runCommand(subCommand, {
  306. rawArgs: opts.rawArgs.slice(subCommandArgIndex + 1)
  307. });
  308. }
  309. } else if (!cmd.run) {
  310. throw new CLIError(`No command specified.`, "E_NO_COMMAND");
  311. }
  312. }
  313. if (typeof cmd.run === "function") {
  314. result = await cmd.run(context);
  315. }
  316. } finally {
  317. if (typeof cmd.cleanup === "function") {
  318. await cmd.cleanup(context);
  319. }
  320. }
  321. return { result };
  322. }
  323. async function resolveSubCommand(cmd, rawArgs, parent) {
  324. const subCommands = await resolveValue(cmd.subCommands);
  325. if (subCommands && Object.keys(subCommands).length > 0) {
  326. const subCommandArgIndex = rawArgs.findIndex((arg) => !arg.startsWith("-"));
  327. const subCommandName = rawArgs[subCommandArgIndex];
  328. const subCommand = await resolveValue(subCommands[subCommandName]);
  329. if (subCommand) {
  330. return resolveSubCommand(
  331. subCommand,
  332. rawArgs.slice(subCommandArgIndex + 1),
  333. cmd
  334. );
  335. }
  336. }
  337. return [cmd, parent];
  338. }
  339. async function showUsage(cmd, parent) {
  340. try {
  341. consola__default.log(await renderUsage(cmd, parent) + "\n");
  342. } catch (error) {
  343. consola__default.error(error);
  344. }
  345. }
  346. async function renderUsage(cmd, parent) {
  347. const cmdMeta = await resolveValue(cmd.meta || {});
  348. const cmdArgs = resolveArgs(await resolveValue(cmd.args || {}));
  349. const parentMeta = await resolveValue(parent?.meta || {});
  350. const commandName = `${parentMeta.name ? `${parentMeta.name} ` : ""}` + (cmdMeta.name || process.argv[1]);
  351. const argLines = [];
  352. const posLines = [];
  353. const commandsLines = [];
  354. const usageLine = [];
  355. for (const arg of cmdArgs) {
  356. if (arg.type === "positional") {
  357. const name = arg.name.toUpperCase();
  358. const isRequired = arg.required !== false && arg.default === void 0;
  359. const defaultHint = arg.default ? `="${arg.default}"` : "";
  360. posLines.push([
  361. "`" + name + defaultHint + "`",
  362. arg.description || "",
  363. arg.valueHint ? `<${arg.valueHint}>` : ""
  364. ]);
  365. usageLine.push(isRequired ? `<${name}>` : `[${name}]`);
  366. } else {
  367. const isRequired = arg.required === true && arg.default === void 0;
  368. const argStr = (arg.type === "boolean" && arg.default === true ? [
  369. ...(arg.alias || []).map((a) => `--no-${a}`),
  370. `--no-${arg.name}`
  371. ].join(", ") : [...(arg.alias || []).map((a) => `-${a}`), `--${arg.name}`].join(
  372. ", "
  373. )) + (arg.type === "string" && (arg.valueHint || arg.default) ? `=${arg.valueHint ? `<${arg.valueHint}>` : `"${arg.default || ""}"`}` : "");
  374. argLines.push([
  375. "`" + argStr + (isRequired ? " (required)" : "") + "`",
  376. arg.description || ""
  377. ]);
  378. if (isRequired) {
  379. usageLine.push(argStr);
  380. }
  381. }
  382. }
  383. if (cmd.subCommands) {
  384. const commandNames = [];
  385. const subCommands = await resolveValue(cmd.subCommands);
  386. for (const [name, sub] of Object.entries(subCommands)) {
  387. const subCmd = await resolveValue(sub);
  388. const meta = await resolveValue(subCmd?.meta);
  389. commandsLines.push([`\`${name}\``, meta?.description || ""]);
  390. commandNames.push(name);
  391. }
  392. usageLine.push(commandNames.join("|"));
  393. }
  394. const usageLines = [];
  395. const version = cmdMeta.version || parentMeta.version;
  396. usageLines.push(
  397. utils.colors.gray(
  398. `${cmdMeta.description} (${commandName + (version ? ` v${version}` : "")})`
  399. ),
  400. ""
  401. );
  402. const hasOptions = argLines.length > 0 || posLines.length > 0;
  403. usageLines.push(
  404. `${utils.colors.underline(utils.colors.bold("USAGE"))} \`${commandName}${hasOptions ? " [OPTIONS]" : ""} ${usageLine.join(" ")}\``,
  405. ""
  406. );
  407. if (posLines.length > 0) {
  408. usageLines.push(utils.colors.underline(utils.colors.bold("ARGUMENTS")), "");
  409. usageLines.push(formatLineColumns(posLines, " "));
  410. usageLines.push("");
  411. }
  412. if (argLines.length > 0) {
  413. usageLines.push(utils.colors.underline(utils.colors.bold("OPTIONS")), "");
  414. usageLines.push(formatLineColumns(argLines, " "));
  415. usageLines.push("");
  416. }
  417. if (commandsLines.length > 0) {
  418. usageLines.push(utils.colors.underline(utils.colors.bold("COMMANDS")), "");
  419. usageLines.push(formatLineColumns(commandsLines, " "));
  420. usageLines.push(
  421. "",
  422. `Use \`${commandName} <command> --help\` for more information about a command.`
  423. );
  424. }
  425. return usageLines.filter((l) => typeof l === "string").join("\n");
  426. }
  427. async function runMain(cmd, opts = {}) {
  428. const rawArgs = opts.rawArgs || process.argv.slice(2);
  429. const showUsage$1 = opts.showUsage || showUsage;
  430. try {
  431. if (rawArgs.includes("--help") || rawArgs.includes("-h")) {
  432. await showUsage$1(...await resolveSubCommand(cmd, rawArgs));
  433. process.exit(0);
  434. } else if (rawArgs.length === 1 && rawArgs[0] === "--version") {
  435. const meta = typeof cmd.meta === "function" ? await cmd.meta() : await cmd.meta;
  436. if (!meta?.version) {
  437. throw new CLIError("No version specified", "E_NO_VERSION");
  438. }
  439. consola__default.log(meta.version);
  440. } else {
  441. await runCommand(cmd, { rawArgs });
  442. }
  443. } catch (error) {
  444. const isCLIError = error instanceof CLIError;
  445. if (!isCLIError) {
  446. consola__default.error(error, "\n");
  447. }
  448. if (isCLIError) {
  449. await showUsage$1(...await resolveSubCommand(cmd, rawArgs));
  450. }
  451. consola__default.error(error.message);
  452. process.exit(1);
  453. }
  454. }
  455. function createMain(cmd) {
  456. return (opts = {}) => runMain(cmd, opts);
  457. }
  458. exports.createMain = createMain;
  459. exports.defineCommand = defineCommand;
  460. exports.parseArgs = parseArgs;
  461. exports.renderUsage = renderUsage;
  462. exports.runCommand = runCommand;
  463. exports.runMain = runMain;
  464. exports.showUsage = showUsage;