UNPKG

22.1 kBJavaScriptView Raw
1// src/node/lib.ts
2import { spawn } from "child_process";
3import { tmpdir } from "os";
4import { resolve } from "path";
5import https from "https";
6import fs from "fs";
7
8// package.json
9var version = "1.0.0";
10
11// src/shared.ts
12var USER_AGENT = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36`;
13
14// node_modules/.pnpm/cac@6.7.12/node_modules/cac/dist/index.mjs
15import { EventEmitter } from "events";
16function toArr(any) {
17 return any == null ? [] : Array.isArray(any) ? any : [any];
18}
19function toVal(out, key, val, opts) {
20 var x, old = out[key], nxt = !!~opts.string.indexOf(key) ? val == null || 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;
21 out[key] = old == null ? nxt : Array.isArray(old) ? old.concat(nxt) : [old, nxt];
22}
23function mri2(args2, opts) {
24 args2 = args2 || [];
25 opts = opts || {};
26 var k, arr, arg, name, val, out = { _: [] };
27 var i = 0, j = 0, idx = 0, len = args2.length;
28 const alibi = opts.alias !== void 0;
29 const strict = opts.unknown !== void 0;
30 const defaults = opts.default !== void 0;
31 opts.alias = opts.alias || {};
32 opts.string = toArr(opts.string);
33 opts.boolean = toArr(opts.boolean);
34 if (alibi) {
35 for (k in opts.alias) {
36 arr = opts.alias[k] = toArr(opts.alias[k]);
37 for (i = 0; i < arr.length; i++) {
38 (opts.alias[arr[i]] = arr.concat(k)).splice(i, 1);
39 }
40 }
41 }
42 for (i = opts.boolean.length; i-- > 0; ) {
43 arr = opts.alias[opts.boolean[i]] || [];
44 for (j = arr.length; j-- > 0; )
45 opts.boolean.push(arr[j]);
46 }
47 for (i = opts.string.length; i-- > 0; ) {
48 arr = opts.alias[opts.string[i]] || [];
49 for (j = arr.length; j-- > 0; )
50 opts.string.push(arr[j]);
51 }
52 if (defaults) {
53 for (k in opts.default) {
54 name = typeof opts.default[k];
55 arr = opts.alias[k] = opts.alias[k] || [];
56 if (opts[name] !== void 0) {
57 opts[name].push(k);
58 for (i = 0; i < arr.length; i++) {
59 opts[name].push(arr[i]);
60 }
61 }
62 }
63 }
64 const keys = strict ? Object.keys(opts.alias) : [];
65 for (i = 0; i < len; i++) {
66 arg = args2[i];
67 if (arg === "--") {
68 out._ = out._.concat(args2.slice(++i));
69 break;
70 }
71 for (j = 0; j < arg.length; j++) {
72 if (arg.charCodeAt(j) !== 45)
73 break;
74 }
75 if (j === 0) {
76 out._.push(arg);
77 } else if (arg.substring(j, j + 3) === "no-") {
78 name = arg.substring(j + 3);
79 if (strict && !~keys.indexOf(name)) {
80 return opts.unknown(arg);
81 }
82 out[name] = false;
83 } else {
84 for (idx = j + 1; idx < arg.length; idx++) {
85 if (arg.charCodeAt(idx) === 61)
86 break;
87 }
88 name = arg.substring(j, idx);
89 val = arg.substring(++idx) || (i + 1 === len || ("" + args2[i + 1]).charCodeAt(0) === 45 || args2[++i]);
90 arr = j === 2 ? [name] : name;
91 for (idx = 0; idx < arr.length; idx++) {
92 name = arr[idx];
93 if (strict && !~keys.indexOf(name))
94 return opts.unknown("-".repeat(j) + name);
95 toVal(out, name, idx + 1 < arr.length || val, opts);
96 }
97 }
98 }
99 if (defaults) {
100 for (k in opts.default) {
101 if (out[k] === void 0) {
102 out[k] = opts.default[k];
103 }
104 }
105 }
106 if (alibi) {
107 for (k in out) {
108 arr = opts.alias[k] || [];
109 while (arr.length > 0) {
110 out[arr.shift()] = out[k];
111 }
112 }
113 }
114 return out;
115}
116var removeBrackets = (v) => v.replace(/[<[].+/, "").trim();
117var findAllBrackets = (v) => {
118 const ANGLED_BRACKET_RE_GLOBAL = /<([^>]+)>/g;
119 const SQUARE_BRACKET_RE_GLOBAL = /\[([^\]]+)\]/g;
120 const res = [];
121 const parse = (match) => {
122 let variadic = false;
123 let value = match[1];
124 if (value.startsWith("...")) {
125 value = value.slice(3);
126 variadic = true;
127 }
128 return {
129 required: match[0].startsWith("<"),
130 value,
131 variadic
132 };
133 };
134 let angledMatch;
135 while (angledMatch = ANGLED_BRACKET_RE_GLOBAL.exec(v)) {
136 res.push(parse(angledMatch));
137 }
138 let squareMatch;
139 while (squareMatch = SQUARE_BRACKET_RE_GLOBAL.exec(v)) {
140 res.push(parse(squareMatch));
141 }
142 return res;
143};
144var getMriOptions = (options) => {
145 const result = { alias: {}, boolean: [] };
146 for (const [index, option] of options.entries()) {
147 if (option.names.length > 1) {
148 result.alias[option.names[0]] = option.names.slice(1);
149 }
150 if (option.isBoolean) {
151 if (option.negated) {
152 const hasStringTypeOption = options.some((o, i) => {
153 return i !== index && o.names.some((name) => option.names.includes(name)) && typeof o.required === "boolean";
154 });
155 if (!hasStringTypeOption) {
156 result.boolean.push(option.names[0]);
157 }
158 } else {
159 result.boolean.push(option.names[0]);
160 }
161 }
162 }
163 return result;
164};
165var findLongest = (arr) => {
166 return arr.sort((a, b) => {
167 return a.length > b.length ? -1 : 1;
168 })[0];
169};
170var padRight = (str, length) => {
171 return str.length >= length ? str : `${str}${" ".repeat(length - str.length)}`;
172};
173var camelcase = (input) => {
174 return input.replace(/([a-z])-([a-z])/g, (_, p1, p2) => {
175 return p1 + p2.toUpperCase();
176 });
177};
178var setDotProp = (obj, keys, val) => {
179 let i = 0;
180 let length = keys.length;
181 let t = obj;
182 let x;
183 for (; i < length; ++i) {
184 x = t[keys[i]];
185 t = t[keys[i]] = i === length - 1 ? val : x != null ? x : !!~keys[i + 1].indexOf(".") || !(+keys[i + 1] > -1) ? {} : [];
186 }
187};
188var setByType = (obj, transforms) => {
189 for (const key of Object.keys(transforms)) {
190 const transform = transforms[key];
191 if (transform.shouldTransform) {
192 obj[key] = Array.prototype.concat.call([], obj[key]);
193 if (typeof transform.transformFunction === "function") {
194 obj[key] = obj[key].map(transform.transformFunction);
195 }
196 }
197 }
198};
199var getFileName = (input) => {
200 const m = /([^\\\/]+)$/.exec(input);
201 return m ? m[1] : "";
202};
203var camelcaseOptionName = (name) => {
204 return name.split(".").map((v, i) => {
205 return i === 0 ? camelcase(v) : v;
206 }).join(".");
207};
208var CACError = class extends Error {
209 constructor(message) {
210 super(message);
211 this.name = this.constructor.name;
212 if (typeof Error.captureStackTrace === "function") {
213 Error.captureStackTrace(this, this.constructor);
214 } else {
215 this.stack = new Error(message).stack;
216 }
217 }
218};
219var Option = class {
220 constructor(rawName, description, config) {
221 this.rawName = rawName;
222 this.description = description;
223 this.config = Object.assign({}, config);
224 rawName = rawName.replace(/\.\*/g, "");
225 this.negated = false;
226 this.names = removeBrackets(rawName).split(",").map((v) => {
227 let name = v.trim().replace(/^-{1,2}/, "");
228 if (name.startsWith("no-")) {
229 this.negated = true;
230 name = name.replace(/^no-/, "");
231 }
232 return camelcaseOptionName(name);
233 }).sort((a, b) => a.length > b.length ? 1 : -1);
234 this.name = this.names[this.names.length - 1];
235 if (this.negated && this.config.default == null) {
236 this.config.default = true;
237 }
238 if (rawName.includes("<")) {
239 this.required = true;
240 } else if (rawName.includes("[")) {
241 this.required = false;
242 } else {
243 this.isBoolean = true;
244 }
245 }
246};
247var processArgs = process.argv;
248var platformInfo = `${process.platform}-${process.arch} node-${process.version}`;
249var Command = class {
250 constructor(rawName, description, config = {}, cli) {
251 this.rawName = rawName;
252 this.description = description;
253 this.config = config;
254 this.cli = cli;
255 this.options = [];
256 this.aliasNames = [];
257 this.name = removeBrackets(rawName);
258 this.args = findAllBrackets(rawName);
259 this.examples = [];
260 }
261 usage(text) {
262 this.usageText = text;
263 return this;
264 }
265 allowUnknownOptions() {
266 this.config.allowUnknownOptions = true;
267 return this;
268 }
269 ignoreOptionDefaultValue() {
270 this.config.ignoreOptionDefaultValue = true;
271 return this;
272 }
273 version(version2, customFlags = "-v, --version") {
274 this.versionNumber = version2;
275 this.option(customFlags, "Display version number");
276 return this;
277 }
278 example(example) {
279 this.examples.push(example);
280 return this;
281 }
282 option(rawName, description, config) {
283 const option = new Option(rawName, description, config);
284 this.options.push(option);
285 return this;
286 }
287 alias(name) {
288 this.aliasNames.push(name);
289 return this;
290 }
291 action(callback) {
292 this.commandAction = callback;
293 return this;
294 }
295 isMatched(name) {
296 return this.name === name || this.aliasNames.includes(name);
297 }
298 get isDefaultCommand() {
299 return this.name === "" || this.aliasNames.includes("!");
300 }
301 get isGlobalCommand() {
302 return this instanceof GlobalCommand;
303 }
304 hasOption(name) {
305 name = name.split(".")[0];
306 return this.options.find((option) => {
307 return option.names.includes(name);
308 });
309 }
310 outputHelp() {
311 const { name, commands } = this.cli;
312 const {
313 versionNumber,
314 options: globalOptions,
315 helpCallback
316 } = this.cli.globalCommand;
317 let sections = [
318 {
319 body: `${name}${versionNumber ? `/${versionNumber}` : ""}`
320 }
321 ];
322 sections.push({
323 title: "Usage",
324 body: ` $ ${name} ${this.usageText || this.rawName}`
325 });
326 const showCommands = (this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0;
327 if (showCommands) {
328 const longestCommandName = findLongest(commands.map((command) => command.rawName));
329 sections.push({
330 title: "Commands",
331 body: commands.map((command) => {
332 return ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;
333 }).join("\n")
334 });
335 sections.push({
336 title: `For more info, run any command with the \`--help\` flag`,
337 body: commands.map((command) => ` $ ${name}${command.name === "" ? "" : ` ${command.name}`} --help`).join("\n")
338 });
339 }
340 let options = this.isGlobalCommand ? globalOptions : [...this.options, ...globalOptions || []];
341 if (!this.isGlobalCommand && !this.isDefaultCommand) {
342 options = options.filter((option) => option.name !== "version");
343 }
344 if (options.length > 0) {
345 const longestOptionName = findLongest(options.map((option) => option.rawName));
346 sections.push({
347 title: "Options",
348 body: options.map((option) => {
349 return ` ${padRight(option.rawName, longestOptionName.length)} ${option.description} ${option.config.default === void 0 ? "" : `(default: ${option.config.default})`}`;
350 }).join("\n")
351 });
352 }
353 if (this.examples.length > 0) {
354 sections.push({
355 title: "Examples",
356 body: this.examples.map((example) => {
357 if (typeof example === "function") {
358 return example(name);
359 }
360 return example;
361 }).join("\n")
362 });
363 }
364 if (helpCallback) {
365 sections = helpCallback(sections) || sections;
366 }
367 console.log(sections.map((section) => {
368 return section.title ? `${section.title}:
369${section.body}` : section.body;
370 }).join("\n\n"));
371 }
372 outputVersion() {
373 const { name } = this.cli;
374 const { versionNumber } = this.cli.globalCommand;
375 if (versionNumber) {
376 console.log(`${name}/${versionNumber} ${platformInfo}`);
377 }
378 }
379 checkRequiredArgs() {
380 const minimalArgsCount = this.args.filter((arg) => arg.required).length;
381 if (this.cli.args.length < minimalArgsCount) {
382 throw new CACError(`missing required args for command \`${this.rawName}\``);
383 }
384 }
385 checkUnknownOptions() {
386 const { options, globalCommand } = this.cli;
387 if (!this.config.allowUnknownOptions) {
388 for (const name of Object.keys(options)) {
389 if (name !== "--" && !this.hasOption(name) && !globalCommand.hasOption(name)) {
390 throw new CACError(`Unknown option \`${name.length > 1 ? `--${name}` : `-${name}`}\``);
391 }
392 }
393 }
394 }
395 checkOptionValue() {
396 const { options: parsedOptions, globalCommand } = this.cli;
397 const options = [...globalCommand.options, ...this.options];
398 for (const option of options) {
399 const value = parsedOptions[option.name.split(".")[0]];
400 if (option.required) {
401 const hasNegated = options.some((o) => o.negated && o.names.includes(option.name));
402 if (value === true || value === false && !hasNegated) {
403 throw new CACError(`option \`${option.rawName}\` value is missing`);
404 }
405 }
406 }
407 }
408};
409var GlobalCommand = class extends Command {
410 constructor(cli) {
411 super("@@global@@", "", {}, cli);
412 }
413};
414var __assign = Object.assign;
415var CAC = class extends EventEmitter {
416 constructor(name = "") {
417 super();
418 this.name = name;
419 this.commands = [];
420 this.rawArgs = [];
421 this.args = [];
422 this.options = {};
423 this.globalCommand = new GlobalCommand(this);
424 this.globalCommand.usage("<command> [options]");
425 }
426 usage(text) {
427 this.globalCommand.usage(text);
428 return this;
429 }
430 command(rawName, description, config) {
431 const command = new Command(rawName, description || "", config, this);
432 command.globalCommand = this.globalCommand;
433 this.commands.push(command);
434 return command;
435 }
436 option(rawName, description, config) {
437 this.globalCommand.option(rawName, description, config);
438 return this;
439 }
440 help(callback) {
441 this.globalCommand.option("-h, --help", "Display this message");
442 this.globalCommand.helpCallback = callback;
443 this.showHelpOnExit = true;
444 return this;
445 }
446 version(version2, customFlags = "-v, --version") {
447 this.globalCommand.version(version2, customFlags);
448 this.showVersionOnExit = true;
449 return this;
450 }
451 example(example) {
452 this.globalCommand.example(example);
453 return this;
454 }
455 outputHelp() {
456 if (this.matchedCommand) {
457 this.matchedCommand.outputHelp();
458 } else {
459 this.globalCommand.outputHelp();
460 }
461 }
462 outputVersion() {
463 this.globalCommand.outputVersion();
464 }
465 setParsedInfo({ args: args2, options }, matchedCommand, matchedCommandName) {
466 this.args = args2;
467 this.options = options;
468 if (matchedCommand) {
469 this.matchedCommand = matchedCommand;
470 }
471 if (matchedCommandName) {
472 this.matchedCommandName = matchedCommandName;
473 }
474 return this;
475 }
476 unsetMatchedCommand() {
477 this.matchedCommand = void 0;
478 this.matchedCommandName = void 0;
479 }
480 parse(argv = processArgs, {
481 run = true
482 } = {}) {
483 this.rawArgs = argv;
484 if (!this.name) {
485 this.name = argv[1] ? getFileName(argv[1]) : "cli";
486 }
487 let shouldParse = true;
488 for (const command of this.commands) {
489 const parsed = this.mri(argv.slice(2), command);
490 const commandName = parsed.args[0];
491 if (command.isMatched(commandName)) {
492 shouldParse = false;
493 const parsedInfo = __assign(__assign({}, parsed), {
494 args: parsed.args.slice(1)
495 });
496 this.setParsedInfo(parsedInfo, command, commandName);
497 this.emit(`command:${commandName}`, command);
498 }
499 }
500 if (shouldParse) {
501 for (const command of this.commands) {
502 if (command.name === "") {
503 shouldParse = false;
504 const parsed = this.mri(argv.slice(2), command);
505 this.setParsedInfo(parsed, command);
506 this.emit(`command:!`, command);
507 }
508 }
509 }
510 if (shouldParse) {
511 const parsed = this.mri(argv.slice(2));
512 this.setParsedInfo(parsed);
513 }
514 if (this.options.help && this.showHelpOnExit) {
515 this.outputHelp();
516 run = false;
517 this.unsetMatchedCommand();
518 }
519 if (this.options.version && this.showVersionOnExit && this.matchedCommandName == null) {
520 this.outputVersion();
521 run = false;
522 this.unsetMatchedCommand();
523 }
524 const parsedArgv = { args: this.args, options: this.options };
525 if (run) {
526 this.runMatchedCommand();
527 }
528 if (!this.matchedCommand && this.args[0]) {
529 this.emit("command:*");
530 }
531 return parsedArgv;
532 }
533 mri(argv, command) {
534 const cliOptions = [
535 ...this.globalCommand.options,
536 ...command ? command.options : []
537 ];
538 const mriOptions = getMriOptions(cliOptions);
539 let argsAfterDoubleDashes = [];
540 const doubleDashesIndex = argv.indexOf("--");
541 if (doubleDashesIndex > -1) {
542 argsAfterDoubleDashes = argv.slice(doubleDashesIndex + 1);
543 argv = argv.slice(0, doubleDashesIndex);
544 }
545 let parsed = mri2(argv, mriOptions);
546 parsed = Object.keys(parsed).reduce((res, name) => {
547 return __assign(__assign({}, res), {
548 [camelcaseOptionName(name)]: parsed[name]
549 });
550 }, { _: [] });
551 const args2 = parsed._;
552 const options = {
553 "--": argsAfterDoubleDashes
554 };
555 const ignoreDefault = command && command.config.ignoreOptionDefaultValue ? command.config.ignoreOptionDefaultValue : this.globalCommand.config.ignoreOptionDefaultValue;
556 let transforms = Object.create(null);
557 for (const cliOption of cliOptions) {
558 if (!ignoreDefault && cliOption.config.default !== void 0) {
559 for (const name of cliOption.names) {
560 options[name] = cliOption.config.default;
561 }
562 }
563 if (Array.isArray(cliOption.config.type)) {
564 if (transforms[cliOption.name] === void 0) {
565 transforms[cliOption.name] = Object.create(null);
566 transforms[cliOption.name]["shouldTransform"] = true;
567 transforms[cliOption.name]["transformFunction"] = cliOption.config.type[0];
568 }
569 }
570 }
571 for (const key of Object.keys(parsed)) {
572 if (key !== "_") {
573 const keys = key.split(".");
574 setDotProp(options, keys, parsed[key]);
575 setByType(options, transforms);
576 }
577 }
578 return {
579 args: args2,
580 options
581 };
582 }
583 runMatchedCommand() {
584 const { args: args2, options, matchedCommand: command } = this;
585 if (!command || !command.commandAction)
586 return;
587 command.checkUnknownOptions();
588 command.checkOptionValue();
589 command.checkRequiredArgs();
590 const actionArgs = [];
591 command.args.forEach((arg, index) => {
592 if (arg.variadic) {
593 actionArgs.push(args2.slice(index));
594 } else {
595 actionArgs.push(args2[index]);
596 }
597 });
598 actionArgs.push(options);
599 return command.commandAction.apply(this, actionArgs);
600 }
601};
602var cac = (name = "") => new CAC(name);
603
604// src/node/lib.ts
605var fetchJSON = (url) => new Promise((resolve2, reject) => {
606 https.get(url, {
607 headers: {
608 "user-agent": USER_AGENT
609 }
610 }, (res) => {
611 let body = "";
612 res.on("data", (chunk) => {
613 body += chunk;
614 });
615 res.on("end", () => {
616 resolve2(JSON.parse(body));
617 });
618 res.on("error", reject);
619 });
620});
621var downloadFile = (url, outFile) => new Promise((resolve2, reject) => {
622 const stream = fs.createWriteStream(outFile);
623 stream.on("finish", () => {
624 stream.close();
625 resolve2(true);
626 });
627 https.get(url).on("response", (res) => {
628 res.pipe(stream, { end: true });
629 }).on("error", reject);
630});
631var ensureDir = (dir) => fs.mkdirSync(dir, { recursive: true });
632var existsSync = fs.existsSync;
633var isEmptyDir = (dir) => fs.readdirSync(dir).length === 0;
634var getTempDir = () => tmpdir();
635function runCommand(cmd) {
636 return new Promise((resolve2, reject) => {
637 const ps = spawn(cmd[0], cmd.slice(1), {
638 stdio: "pipe"
639 });
640 let output = "";
641 ps.stdout.on("data", (data) => {
642 output += data.toString();
643 });
644 ps.stderr.on("data", (data) => {
645 output += data.toString();
646 });
647 ps.on("exit", (code) => {
648 if (code === 0) {
649 resolve2(true);
650 } else {
651 reject(new Error(output));
652 }
653 });
654 });
655}
656var resolvePath = resolve;
657var args = process.argv;
658var exit = process.exit;
659var getOwnVersion = () => {
660 return version;
661};
662
663// src/index.ts
664var start = async () => {
665 const cli = cac("aho");
666 cli.command("[repo] [desitination]", "Download a repo").option("-f, --force", `Force override desitination directory even if it's not empty`).action(async (_repo, desitination, flags) => {
667 if (!_repo) {
668 throw new PrettyError("No repo provided");
669 }
670 const { repo, tag } = parseRepo(_repo);
671 desitination = resolvePath(desitination || ".");
672 if (!flags.force && existsSync(desitination) && !isEmptyDir(desitination)) {
673 throw new PrettyError(`Destination directory is not empty, use --force if you are sure about this`);
674 }
675 console.log(`Downloading ${repo}`);
676 const ref = tag || await getDefaultBranchFromApi(repo);
677 const tempTarFile = `${getTempDir()}/aho_${Date.now()}.tar.gz`;
678 await downloadFile(`https://codeload.github.com/${repo}/tar.gz/refs/heads/${ref}`, tempTarFile);
679 console.log(`Generating to ${desitination}`);
680 await extract(tempTarFile, desitination);
681 });
682 cli.version(getOwnVersion());
683 cli.help();
684 cli.parse(args, { run: false });
685 try {
686 await cli.runMatchedCommand();
687 } catch (error) {
688 if (error instanceof PrettyError) {
689 console.error(error.message);
690 } else {
691 console.error(error);
692 }
693 exit(1);
694 }
695};
696var PrettyError = class extends Error {
697 constructor(message) {
698 super(message);
699 this.name = this.constructor.name;
700 if (typeof Error.captureStackTrace === "function") {
701 Error.captureStackTrace(this, this.constructor);
702 } else {
703 this.stack = new Error(message).stack;
704 }
705 }
706};
707async function extract(from, to) {
708 ensureDir(to);
709 await runCommand(["tar", "xvzf", from, "-C", to, "--strip-components", "1"]);
710}
711function parseRepo(input) {
712 const [repo, tag] = input.split("#");
713 return { repo, tag };
714}
715async function getDefaultBranchFromApi(repo) {
716 const data = await fetchJSON(`https://api.github.com/repos/${repo}`);
717 return data.default_branch;
718}
719export {
720 start
721};